dolibarr 21.0.0-alpha
expensereport.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2011 Dimitri Mouillard <dmouillard@teclib.com>
3 * Copyright (C) 2015 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2015 Alexandre Spangaro <aspangaro@open-dsi.fr>
5 * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
6 * Copyright (c) 2018-2024 Frédéric France <frederic.france@free.fr>
7 * Copyright (C) 2016-2020 Ferran Marcet <fmarcet@2byte.es>
8 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
9 * Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <https://www.gnu.org/licenses/>.
23 */
24
30require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
31require_once DOL_DOCUMENT_ROOT.'/expensereport/class/expensereportline.class.php';
32require_once DOL_DOCUMENT_ROOT.'/expensereport/class/expensereport_ik.class.php';
33require_once DOL_DOCUMENT_ROOT.'/expensereport/class/expensereport_rule.class.php';
34
35
40{
44 public $element = 'expensereport';
45
49 public $table_element = 'expensereport';
50
54 public $table_element_line = 'expensereport_det';
55
59 public $fk_element = 'fk_expensereport';
60
64 public $picto = 'trip';
65
69 public $lines = array();
70
74 public $line;
75
79 public $date_debut;
80
84 public $date_fin;
85
89 public $date_approbation;
90
94 public $fk_user;
95
99 public $user_approve_id;
100
106 public $status;
107
114 public $fk_statut;
115
119 public $fk_c_paiement;
120
124 public $modepaymentid;
125
129 public $paid;
130
131 // Paiement
135 public $user_paid_infos;
136
140 public $user_author_infos;
141
145 public $user_validator_infos;
146
150 public $rule_warning_message;
151
152 // ACTIONS
153
154 // Create
158 public $date_create;
159
163 public $fk_user_creat;
164
168 public $fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
169
170 // Update
174 public $date_modif;
175
179 public $fk_user_modif;
180
181 // Refus
185 public $date_refuse;
186
190 public $detail_refuse;
191
195 public $fk_user_refuse;
196
197 // Annulation
201 public $date_cancel;
202
206 public $detail_cancel;
207
211 public $fk_user_cancel;
212
216 public $fk_user_validator;
217
224 public $datevalid;
225
230 public $date_valid;
231
235 public $fk_user_valid;
236
240 public $user_valid_infos;
241
242 // Approve
246 public $date_approve;
247
251 public $fk_user_approve;
252
257 public $localtax1; // for backward compatibility (real field should be total_localtax1 defined into CommonObject)
262 public $localtax2; // for backward compatibility (real field should be total_localtax2 defined into CommonObject)
263
267 const STATUS_DRAFT = 0;
268
273
278
283
287 const STATUS_CLOSED = 6;
288
292 const STATUS_REFUSED = 99;
293
294 public $fields = array(
295 'rowid' => array('type' => 'integer', 'label' => 'ID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10),
296 'ref' => array('type' => 'varchar(50)', 'label' => 'Ref', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'showoncombobox' => 1, 'position' => 15),
297 'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 20),
298 'ref_number_int' => array('type' => 'integer', 'label' => 'Ref number int', 'enabled' => 1, 'visible' => -1, 'position' => 25),
299 'ref_ext' => array('type' => 'integer', 'label' => 'Ref ext', 'enabled' => 1, 'visible' => -1, 'position' => 30),
300 'total_ht' => array('type' => 'double(24,8)', 'label' => 'Total ht', 'enabled' => 1, 'visible' => -1, 'position' => 35),
301 'total_tva' => array('type' => 'double(24,8)', 'label' => 'Total tva', 'enabled' => 1, 'visible' => -1, 'position' => 40),
302 'localtax1' => array('type' => 'double(24,8)', 'label' => 'Localtax1', 'enabled' => 1, 'visible' => -1, 'position' => 45),
303 'localtax2' => array('type' => 'double(24,8)', 'label' => 'Localtax2', 'enabled' => 1, 'visible' => -1, 'position' => 50),
304 'total_ttc' => array('type' => 'double(24,8)', 'label' => 'Total ttc', 'enabled' => 1, 'visible' => -1, 'position' => 55),
305 'date_debut' => array('type' => 'date', 'label' => 'Date debut', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 60),
306 'date_fin' => array('type' => 'date', 'label' => 'Date fin', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 65),
307 'date_valid' => array('type' => 'datetime', 'label' => 'Date valid', 'enabled' => 1, 'visible' => -1, 'position' => 75),
308 'date_approve' => array('type' => 'datetime', 'label' => 'Date approve', 'enabled' => 1, 'visible' => -1, 'position' => 80),
309 'date_refuse' => array('type' => 'datetime', 'label' => 'Date refuse', 'enabled' => 1, 'visible' => -1, 'position' => 85),
310 'date_cancel' => array('type' => 'datetime', 'label' => 'Date cancel', 'enabled' => 1, 'visible' => -1, 'position' => 90),
311 'fk_user_author' => array('type' => 'integer', 'label' => 'Fk user author', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 100),
312 'fk_user_modif' => array('type' => 'integer', 'label' => 'Fk user modif', 'enabled' => 1, 'visible' => -1, 'position' => 105),
313 'fk_user_valid' => array('type' => 'integer', 'label' => 'Fk user valid', 'enabled' => 1, 'visible' => -1, 'position' => 110),
314 'fk_user_validator' => array('type' => 'integer', 'label' => 'Fk user validator', 'enabled' => 1, 'visible' => -1, 'position' => 115),
315 'fk_user_approve' => array('type' => 'integer', 'label' => 'Fk user approve', 'enabled' => 1, 'visible' => -1, 'position' => 120),
316 'fk_user_refuse' => array('type' => 'integer', 'label' => 'Fk user refuse', 'enabled' => 1, 'visible' => -1, 'position' => 125),
317 'fk_user_cancel' => array('type' => 'integer', 'label' => 'Fk user cancel', 'enabled' => 1, 'visible' => -1, 'position' => 130),
318 'fk_c_paiement' => array('type' => 'integer', 'label' => 'Fk c paiement', 'enabled' => 1, 'visible' => -1, 'position' => 140),
319 'paid' => array('type' => 'integer', 'label' => 'Paid', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 145),
320 'note_public' => array('type' => 'html', 'label' => 'Note public', 'enabled' => 1, 'visible' => 0, 'position' => 150),
321 'note_private' => array('type' => 'html', 'label' => 'Note private', 'enabled' => 1, 'visible' => 0, 'position' => 155),
322 'detail_refuse' => array('type' => 'varchar(255)', 'label' => 'Detail refuse', 'enabled' => 1, 'visible' => -1, 'position' => 160),
323 'detail_cancel' => array('type' => 'varchar(255)', 'label' => 'Detail cancel', 'enabled' => 1, 'visible' => -1, 'position' => 165),
324 'integration_compta' => array('type' => 'integer', 'label' => 'Integration compta', 'enabled' => 1, 'visible' => -1, 'position' => 170),
325 'fk_bank_account' => array('type' => 'integer', 'label' => 'Fk bank account', 'enabled' => 1, 'visible' => -1, 'position' => 175),
326 'fk_multicurrency' => array('type' => 'integer', 'label' => 'Fk multicurrency', 'enabled' => 1, 'visible' => -1, 'position' => 185),
327 'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'Multicurrency code', 'enabled' => 1, 'visible' => -1, 'position' => 190),
328 'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'Multicurrency tx', 'enabled' => 1, 'visible' => -1, 'position' => 195),
329 'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'Multicurrency total ht', 'enabled' => 1, 'visible' => -1, 'position' => 200),
330 'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'Multicurrency total tva', 'enabled' => 1, 'visible' => -1, 'position' => 205),
331 'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'Multicurrency total ttc', 'enabled' => 1, 'visible' => -1, 'position' => 210),
332 'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => -1, 'position' => 220),
333 'date_create' => array('type' => 'datetime', 'label' => 'Date create', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 300),
334 'tms' => array('type' => 'timestamp', 'label' => 'Tms', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 305),
335 'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -1, 'position' => 1000),
336 'model_pdf' => array('type' => 'varchar(255)', 'label' => 'Model pdf', 'enabled' => 1, 'visible' => 0, 'position' => 1010),
337 'fk_statut' => array('type' => 'integer', 'label' => 'Fk statut', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 500),
338 );
339
345 public function __construct($db)
346 {
347 $this->db = $db;
348 $this->total_ht = 0;
349 $this->total_ttc = 0;
350 $this->total_tva = 0;
351 $this->total_localtax1 = 0;
352 $this->total_localtax2 = 0;
353 $this->localtax1 = 0; // For backward compatibility
354 $this->localtax2 = 0; // For backward compatibility
355 $this->modepaymentid = 0;
356
357 // List of language codes for status
358 $this->labelStatusShort = array(0 => 'Draft', 2 => 'Validated', 4 => 'Canceled', 5 => 'Approved', 6 => 'Paid', 99 => 'Refused');
359 $this->labelStatus = array(0 => 'Draft', 2 => 'ValidatedWaitingApproval', 4 => 'Canceled', 5 => 'Approved', 6 => 'Paid', 99 => 'Refused');
360 }
361
369 public function create($user, $notrigger = 0)
370 {
371 global $conf, $langs;
372
373 $now = dol_now();
374
375 $error = 0;
376
377 // Check parameters
378 if (empty($this->date_debut) || empty($this->date_fin)) {
379 $this->error = $langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Date'));
380 return -1;
381 }
382
383 $fuserid = $this->fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
384 if (empty($fuserid)) {
385 $fuserid = $user->id;
386 }
387
388 $this->db->begin();
389
390 $sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
391 $sql .= "ref";
392 $sql .= ",total_ht";
393 $sql .= ",total_ttc";
394 $sql .= ",total_tva";
395 $sql .= ",date_debut";
396 $sql .= ",date_fin";
397 $sql .= ",date_create";
398 $sql .= ",fk_user_creat";
399 $sql .= ",fk_user_author";
400 $sql .= ",fk_user_validator";
401 $sql .= ",fk_user_approve";
402 $sql .= ",fk_user_modif";
403 $sql .= ",fk_statut";
404 $sql .= ",fk_c_paiement";
405 $sql .= ",paid";
406 $sql .= ",note_public";
407 $sql .= ",note_private";
408 $sql .= ",entity";
409 $sql .= ") VALUES(";
410 $sql .= "'(PROV)'";
411 $sql .= ", ".price2num($this->total_ht, 'MT');
412 $sql .= ", ".price2num($this->total_ttc, 'MT');
413 $sql .= ", ".price2num($this->total_tva, 'MT');
414 $sql .= ", '".$this->db->idate($this->date_debut)."'";
415 $sql .= ", '".$this->db->idate($this->date_fin)."'";
416 $sql .= ", '".$this->db->idate($now)."'";
417 $sql .= ", ".((int) $user->id);
418 $sql .= ", ".((int) $fuserid);
419 $sql .= ", ".($this->fk_user_validator > 0 ? ((int) $this->fk_user_validator) : "null");
420 $sql .= ", ".($this->fk_user_approve > 0 ? ((int) $this->fk_user_approve) : "null");
421 $sql .= ", ".($this->fk_user_modif > 0 ? ((int) $this->fk_user_modif) : "null");
422 $sql .= ", ".($this->fk_statut > 1 ? ((int) $this->fk_statut) : 0);
423 $sql .= ", ".($this->modepaymentid ? ((int) $this->modepaymentid) : "null");
424 $sql .= ", 0";
425 $sql .= ", ".($this->note_public ? "'".$this->db->escape($this->note_public)."'" : "null");
426 $sql .= ", ".($this->note_private ? "'".$this->db->escape($this->note_private)."'" : "null");
427 $sql .= ", ".((int) $conf->entity);
428 $sql .= ")";
429
430 $result = $this->db->query($sql);
431 if ($result) {
432 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
433 $this->ref = '(PROV'.$this->id.')';
434
435 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element." SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
436 $resql = $this->db->query($sql);
437 if (!$resql) {
438 $this->error = $this->db->lasterror();
439 $error++;
440 }
441
442 if (!$error) {
443 if (is_array($this->lines) && count($this->lines) > 0) {
444 foreach ($this->lines as $line) {
445 // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
446 //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
447 if (!is_object($line)) {
448 $line = (object) $line;
449 $newndfline = new ExpenseReportLine($this->db);
450 $newndfline->fk_expensereport = $line->fk_expensereport;
451 $newndfline->fk_c_type_fees = $line->fk_c_type_fees;
452 $newndfline->fk_project = $line->fk_project;
453 $newndfline->vatrate = $line->vatrate;
454 $newndfline->vat_src_code = $line->vat_src_code;
455 $newndfline->localtax1_tx = $line->localtax1_tx;
456 $newndfline->localtax2_tx = $line->localtax2_tx;
457 $newndfline->localtax1_type = $line->localtax1_type;
458 $newndfline->localtax2_type = $line->localtax2_type;
459 $newndfline->comments = $line->comments;
460 $newndfline->qty = $line->qty;
461 $newndfline->value_unit = $line->value_unit;
462 $newndfline->total_ht = $line->total_ht;
463 $newndfline->total_ttc = $line->total_ttc;
464 $newndfline->total_tva = $line->total_tva;
465 $newndfline->total_localtax1 = $line->total_localtax1;
466 $newndfline->total_localtax2 = $line->total_localtax2;
467 $newndfline->date = $line->date;
468 $newndfline->rule_warning_message = $line->rule_warning_message;
469 $newndfline->fk_c_exp_tax_cat = $line->fk_c_exp_tax_cat;
470 $newndfline->fk_ecm_files = $line->fk_ecm_files;
471 } else {
472 $newndfline = $line;
473 }
474 //$newndfline=new ExpenseReportLine($this->db);
475 $newndfline->fk_expensereport = $this->id;
476 $result = $newndfline->insert();
477 if ($result < 0) {
478 $this->error = $newndfline->error;
479 $this->errors = $newndfline->errors;
480 $error++;
481 break;
482 }
483 }
484 }
485 }
486
487 if (!$error) {
488 $result = $this->insertExtraFields();
489 if ($result < 0) {
490 $error++;
491 }
492 }
493
494 if (!$error) {
495 $result = $this->update_price(1);
496 if ($result > 0) {
497 if (!$notrigger) {
498 // Call trigger
499 $result = $this->call_trigger('EXPENSE_REPORT_CREATE', $user);
500
501 if ($result < 0) {
502 $error++;
503 }
504 // End call triggers
505 }
506
507 if (empty($error)) {
508 $this->db->commit();
509 return $this->id;
510 } else {
511 $this->db->rollback();
512 return -4;
513 }
514 } else {
515 $this->db->rollback();
516 return -3;
517 }
518 } else {
519 dol_syslog(get_class($this)."::create error ".$this->error, LOG_ERR);
520 $this->db->rollback();
521 return -2;
522 }
523 } else {
524 $this->error = $this->db->lasterror()." sql=".$sql;
525 $this->db->rollback();
526 return -1;
527 }
528 }
529
537 public function createFromClone(User $user, $fk_user_author)
538 {
539 global $hookmanager;
540
541 $error = 0;
542
543 if (empty($fk_user_author)) {
544 $fk_user_author = $user->id;
545 }
546
547 $this->db->begin();
548
549 // get extrafields so they will be clone
550 //foreach($this->lines as $line)
551 //$line->fetch_optionals();
552
553 // Load source object
554 $objFrom = clone $this;
555
556 $this->id = 0;
557 $this->ref = '';
558 $this->status = 0;
559 $this->fk_statut = 0; // deprecated
560
561 // Clear fields
562 $this->fk_user_creat = $user->id;
563 $this->fk_user_author = $fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
564 $this->fk_user_valid = 0;
565 $this->date_create = '';
566 $this->date_creation = '';
567 $this->date_validation = '';
568
569 // Remove link on lines to a joined file
570 if (is_array($this->lines) && count($this->lines) > 0) {
571 foreach ($this->lines as $key => $line) {
572 $this->lines[$key]->fk_ecm_files = 0;
573 }
574 }
575
576 // Create clone
577 $this->context['createfromclone'] = 'createfromclone';
578 $result = $this->create($user);
579 if ($result < 0) {
580 $error++;
581 }
582
583 if (!$error) {
584 // Hook of thirdparty module
585 if (is_object($hookmanager)) {
586 $parameters = array('objFrom' => $objFrom);
587 $action = '';
588 $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
589 if ($reshook < 0) {
590 $this->setErrorsFromObject($hookmanager);
591 $error++;
592 }
593 }
594 }
595
596 unset($this->context['createfromclone']);
597
598 // End
599 if (!$error) {
600 $this->db->commit();
601 return $this->id;
602 } else {
603 $this->db->rollback();
604 return -1;
605 }
606 }
607
608
617 public function update($user, $notrigger = 0, $userofexpensereport = null)
618 {
619 global $langs;
620
621 $error = 0;
622 $this->db->begin();
623
624 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
625 $sql .= " total_ht = ".$this->total_ht;
626 $sql .= " , total_ttc = ".$this->total_ttc;
627 $sql .= " , total_tva = ".$this->total_tva;
628 $sql .= " , date_debut = '".$this->db->idate($this->date_debut)."'";
629 $sql .= " , date_fin = '".$this->db->idate($this->date_fin)."'";
630 if ($userofexpensereport && is_object($userofexpensereport)) {
631 $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.
632 }
633 $sql .= " , fk_user_validator = ".($this->fk_user_validator > 0 ? $this->fk_user_validator : "null");
634 $sql .= " , fk_user_valid = ".($this->fk_user_valid > 0 ? $this->fk_user_valid : "null");
635 $sql .= " , fk_user_approve = ".($this->fk_user_approve > 0 ? $this->fk_user_approve : "null");
636 $sql .= " , fk_user_modif = ".$user->id;
637 $sql .= " , fk_statut = ".($this->fk_statut >= 0 ? $this->fk_statut : '0');
638 $sql .= " , fk_c_paiement = ".($this->fk_c_paiement > 0 ? $this->fk_c_paiement : "null");
639 $sql .= " , note_public = ".(!empty($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "''");
640 $sql .= " , note_private = ".(!empty($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "''");
641 $sql .= " , detail_refuse = ".(!empty($this->detail_refuse) ? "'".$this->db->escape($this->detail_refuse)."'" : "''");
642 $sql .= " WHERE rowid = ".((int) $this->id);
643
644 dol_syslog(get_class($this)."::update", LOG_DEBUG);
645 $result = $this->db->query($sql);
646 if ($result) {
647 if (!$notrigger) {
648 // Call trigger
649 $result = $this->call_trigger('EXPENSE_REPORT_MODIFY', $user);
650
651 if ($result < 0) {
652 $error++;
653 }
654 // End call triggers
655 }
656
657 if (empty($error)) {
658 $this->db->commit();
659 return 1;
660 } else {
661 $this->db->rollback();
662 $this->error = $this->db->error();
663 return -2;
664 }
665 } else {
666 $this->db->rollback();
667 $this->error = $this->db->error();
668 return -1;
669 }
670 }
671
679 public function fetch($id, $ref = '')
680 {
681 $sql = "SELECT d.rowid, d.entity, d.ref, d.note_public, d.note_private,"; // DEFAULT
682 $sql .= " d.detail_refuse, d.detail_cancel, d.fk_user_refuse, d.fk_user_cancel,"; // ACTIONS
683 $sql .= " d.date_refuse, d.date_cancel,"; // ACTIONS
684 $sql .= " d.total_ht, d.total_ttc, d.total_tva,";
685 $sql .= " d.localtax1 as total_localtax1, d.localtax2 as total_localtax2,";
686 $sql .= " d.date_debut, d.date_fin, d.date_create, d.tms as date_modif, d.date_valid, d.date_approve,"; // DATES (datetime)
687 $sql .= " d.fk_user_creat, d.fk_user_author, d.fk_user_modif, d.fk_user_validator,";
688 $sql .= " d.fk_user_valid, d.fk_user_approve,";
689 $sql .= " d.fk_statut as status, d.fk_c_paiement, d.paid";
690 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as d";
691 if ($ref) {
692 $sql .= " WHERE d.ref = '".$this->db->escape($ref)."'";
693 } else {
694 $sql .= " WHERE d.rowid = ".((int) $id);
695 }
696 //$sql.= $restrict;
697
698 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
699 $resql = $this->db->query($sql);
700 if ($resql) {
701 $obj = $this->db->fetch_object($resql);
702 if ($obj) {
703 $this->id = $obj->rowid;
704 $this->ref = $obj->ref;
705
706 $this->entity = $obj->entity;
707
708 $this->total_ht = $obj->total_ht;
709 $this->total_tva = $obj->total_tva;
710 $this->total_ttc = $obj->total_ttc;
711 $this->localtax1 = $obj->total_localtax1; // For backward compatibility
712 $this->localtax2 = $obj->total_localtax2; // For backward compatibility
713 $this->total_localtax1 = $obj->total_localtax1;
714 $this->total_localtax2 = $obj->total_localtax2;
715
716 $this->note_public = $obj->note_public;
717 $this->note_private = $obj->note_private;
718 $this->detail_refuse = $obj->detail_refuse;
719 $this->detail_cancel = $obj->detail_cancel;
720
721 $this->date_debut = $this->db->jdate($obj->date_debut);
722 $this->date_fin = $this->db->jdate($obj->date_fin);
723 $this->date_valid = $this->db->jdate($obj->date_valid);
724 $this->date_approve = $this->db->jdate($obj->date_approve);
725 $this->date_create = $this->db->jdate($obj->date_create);
726 $this->date_modif = $this->db->jdate($obj->date_modif);
727 $this->date_refuse = $this->db->jdate($obj->date_refuse);
728 $this->date_cancel = $this->db->jdate($obj->date_cancel);
729
730 $this->fk_user_creat = $obj->fk_user_creat;
731 $this->fk_user_author = $obj->fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
732 $this->fk_user_modif = $obj->fk_user_modif;
733 $this->fk_user_validator = $obj->fk_user_validator;
734 $this->fk_user_valid = $obj->fk_user_valid;
735 $this->fk_user_refuse = $obj->fk_user_refuse;
736 $this->fk_user_cancel = $obj->fk_user_cancel;
737 $this->fk_user_approve = $obj->fk_user_approve;
738
739 $user_author = new User($this->db);
740 if ($this->fk_user_author > 0) {
741 $user_author->fetch($this->fk_user_author);
742 }
743
744 $this->user_author_infos = dolGetFirstLastname($user_author->firstname, $user_author->lastname);
745
746 $user_approver = new User($this->db);
747 if ($this->fk_user_approve > 0) {
748 $user_approver->fetch($this->fk_user_approve);
749 } elseif ($this->fk_user_validator > 0) {
750 $user_approver->fetch($this->fk_user_validator); // For backward compatibility
751 }
752 $this->user_validator_infos = dolGetFirstLastname($user_approver->firstname, $user_approver->lastname);
753
754 $this->fk_statut = $obj->status; // deprecated
755 $this->status = $obj->status;
756 $this->fk_c_paiement = $obj->fk_c_paiement;
757 $this->paid = $obj->paid;
758
759 if ($this->status == self::STATUS_APPROVED || $this->status == self::STATUS_CLOSED) {
760 $user_valid = new User($this->db);
761 if ($this->fk_user_valid > 0) {
762 $user_valid->fetch($this->fk_user_valid);
763 }
764 $this->user_valid_infos = dolGetFirstLastname($user_valid->firstname, $user_valid->lastname);
765 }
766
767 $this->fetch_optionals();
768
769 $result = $this->fetch_lines();
770
771 return $result;
772 } else {
773 return 0;
774 }
775 } else {
776 $this->error = $this->db->lasterror();
777 return -1;
778 }
779 }
780
781 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
792 public function set_paid($id, $fuser, $notrigger = 0)
793 {
794 // phpcs:enable
795 dol_syslog(get_class($this)."::set_paid is deprecated, use setPaid instead", LOG_NOTICE);
796 return $this->setPaid($id, $fuser, $notrigger);
797 }
798
807 public function setPaid($id, $fuser, $notrigger = 0)
808 {
809 $error = 0;
810 $this->db->begin();
811
812 $sql = "UPDATE ".MAIN_DB_PREFIX."expensereport";
813 $sql .= " SET fk_statut = ".self::STATUS_CLOSED.", paid=1";
814 $sql .= " WHERE rowid = ".((int) $id)." AND fk_statut = ".self::STATUS_APPROVED;
815
816 dol_syslog(get_class($this)."::setPaid", LOG_DEBUG);
817 $resql = $this->db->query($sql);
818 if ($resql) {
819 if ($this->db->affected_rows($resql)) {
820 if (!$notrigger) {
821 // Call trigger
822 $result = $this->call_trigger('EXPENSE_REPORT_PAID', $fuser);
823
824 if ($result < 0) {
825 $error++;
826 }
827 // End call triggers
828 }
829
830 if (empty($error)) {
831 $this->db->commit();
832 return 1;
833 } else {
834 $this->db->rollback();
835 $this->error = $this->db->error();
836 return -2;
837 }
838 } else {
839 $this->db->commit();
840 return 0;
841 }
842 } else {
843 $this->db->rollback();
844 dol_print_error($this->db);
845 return -1;
846 }
847 }
848
855 public function getLibStatut($mode = 0)
856 {
857 return $this->LibStatut($this->status, $mode);
858 }
859
860 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
868 public function LibStatut($status, $mode = 0)
869 {
870 // phpcs:enable
871 global $langs;
872
873 $labelStatus = $langs->transnoentitiesnoconv($this->labelStatus[$status]);
874 $labelStatusShort = $langs->transnoentitiesnoconv($this->labelStatusShort[$status]);
875
876 $statuslogo = array(0 => 'status0', 2 => 'status1', 4 => 'status6', 5 => 'status4', 6 => 'status6', 99 => 'status5');
877
878 $statusType = $statuslogo[$status];
879
880 return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode);
881 }
882
883
890 public function info($id)
891 {
892 global $conf;
893
894 $sql = "SELECT f.rowid,";
895 $sql .= " f.date_create as datec,";
896 $sql .= " f.tms as date_modification,";
897 $sql .= " f.date_valid as datev,";
898 $sql .= " f.date_approve as datea,";
899 $sql .= " f.fk_user_creat as fk_user_creation,";
900 $sql .= " f.fk_user_author as fk_user_author,";
901 $sql .= " f.fk_user_modif as fk_user_modification,";
902 $sql .= " f.fk_user_valid,";
903 $sql .= " f.fk_user_approve";
904 $sql .= " FROM ".MAIN_DB_PREFIX."expensereport as f";
905 $sql .= " WHERE f.rowid = ".((int) $id);
906 $sql .= " AND f.entity = ".$conf->entity;
907
908
909
910 $resql = $this->db->query($sql);
911 if ($resql) {
912 if ($this->db->num_rows($resql)) {
913 $obj = $this->db->fetch_object($resql);
914
915 $this->id = $obj->rowid;
916
917 $this->date_creation = $this->db->jdate($obj->datec);
918 $this->date_modification = $this->db->jdate($obj->date_modification);
919 $this->date_validation = $this->db->jdate($obj->datev);
920 $this->date_approbation = $this->db->jdate($obj->datea);
921
922 $this->user_creation_id = $obj->fk_user_author;
923 $this->user_creation_id = $obj->fk_user_creation;
924 $this->user_validation_id = $obj->fk_user_valid;
925 $this->user_modification_id = $obj->fk_user_modification;
926 $this->user_approve_id = $obj->fk_user_approve;
927 }
928 $this->db->free($resql);
929 } else {
930 dol_print_error($this->db);
931 }
932 }
933
934
935
943 public function initAsSpecimen()
944 {
945 global $user, $langs;
946
947 $now = dol_now();
948
949 // Initialise parameters
950 $this->id = 0;
951 $this->ref = 'SPECIMEN';
952 $this->specimen = 1;
953 $this->entity = 1;
954 $this->date_create = $now;
955 $this->date_debut = $now;
956 $this->date_fin = $now;
957 $this->date_valid = $now;
958 $this->date_approve = $now;
959
960 $type_fees_id = 2; // TF_TRIP
961
962 $this->status = 5;
963
964 $this->fk_user_author = $user->id;
965 $this->fk_user_validator = $user->id;
966 $this->fk_user_valid = $user->id;
967 $this->fk_user_approve = $user->id;
968
969 $this->note_private = 'Private note';
970 $this->note_public = 'SPECIMEN';
971 $nbp = 5;
972 $xnbp = 0;
973 while ($xnbp < $nbp) {
974 $line = new ExpenseReportLine($this->db);
975 $line->comments = $langs->trans("Comment")." ".$xnbp;
976 $line->date = ($now - 3600 * (1 + $xnbp));
977 $line->total_ht = 100;
978 $line->total_tva = 20;
979 $line->total_ttc = 120;
980 $line->qty = 1;
981 $line->vatrate = 20;
982 $line->value_unit = 120;
983 $line->fk_expensereport = 0;
984 $line->type_fees_code = 'TRA';
985 $line->fk_c_type_fees = $type_fees_id;
986
987 $line->projet_ref = 'ABC';
988
989 $this->lines[$xnbp] = $line;
990 $xnbp++;
991
992 $this->total_ht += $line->total_ht;
993 $this->total_tva += $line->total_tva;
994 $this->total_ttc += $line->total_ttc;
995 }
996
997 return 1;
998 }
999
1000 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1008 public function fetch_line_by_project($projectid, $user)
1009 {
1010 // phpcs:enable
1011 global $langs;
1012
1013 $langs->load('trips');
1014
1015 if ($user->hasRight('expensereport', 'lire')) {
1016 $sql = "SELECT de.fk_expensereport, de.date, de.comments, de.total_ht, de.total_ttc";
1017 $sql .= " FROM ".MAIN_DB_PREFIX."expensereport_det as de";
1018 $sql .= " WHERE de.fk_projet = ".((int) $projectid);
1019
1020 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1021 $result = $this->db->query($sql);
1022 if ($result) {
1023 $num = $this->db->num_rows($result);
1024 $i = 0;
1025 $total_HT = 0;
1026 $total_TTC = 0;
1027
1028 while ($i < $num) {
1029 $objp = $this->db->fetch_object($result);
1030
1031 $sql2 = "SELECT d.rowid, d.fk_user_author, d.ref, d.fk_statut as status";
1032 $sql2 .= " FROM ".MAIN_DB_PREFIX."expensereport as d";
1033 $sql2 .= " WHERE d.rowid = ".((int) $objp->fk_expensereport);
1034
1035 $result2 = $this->db->query($sql2);
1036 $obj = $this->db->fetch_object($result2);
1037
1038 $objp->fk_user_author = $obj->fk_user_author;
1039 $objp->ref = $obj->ref;
1040 $objp->fk_c_expensereport_status = $obj->status;
1041 $objp->rowid = $obj->rowid;
1042
1043 $total_HT += $objp->total_ht;
1044 $total_TTC += $objp->total_ttc;
1045 $author = new User($this->db);
1046 $author->fetch($objp->fk_user_author);
1047
1048 print '<tr>';
1049 print '<td><a href="'.DOL_URL_ROOT.'/expensereport/card.php?id='.$objp->rowid.'">'.$objp->ref_num.'</a></td>';
1050 print '<td class="center">'.dol_print_date($objp->date, 'day').'</td>';
1051 print '<td>'.$author->getNomUrl(1).'</td>';
1052 print '<td>'.$objp->comments.'</td>';
1053 print '<td class="right">'.price($objp->total_ht).'</td>';
1054 print '<td class="right">'.price($objp->total_ttc).'</td>';
1055 print '<td class="right">';
1056
1057 switch ($objp->fk_c_expensereport_status) {
1058 case 4:
1059 print img_picto($langs->trans('StatusOrderCanceled'), 'statut5');
1060 break;
1061 case 1:
1062 print $langs->trans('Draft').' '.img_picto($langs->trans('Draft'), 'statut0');
1063 break;
1064 case 2:
1065 print $langs->trans('TripForValid').' '.img_picto($langs->trans('TripForValid'), 'statut3');
1066 break;
1067 case 5:
1068 print $langs->trans('TripForPaid').' '.img_picto($langs->trans('TripForPaid'), 'statut3');
1069 break;
1070 case 6:
1071 print $langs->trans('TripPaid').' '.img_picto($langs->trans('TripPaid'), 'statut4');
1072 break;
1073 }
1074 /*
1075 if ($status==4) return img_picto($langs->trans('StatusOrderCanceled'),'statut5');
1076 if ($status==1) return img_picto($langs->trans('StatusOrderDraft'),'statut0');
1077 if ($status==2) return img_picto($langs->trans('StatusOrderValidated'),'statut1');
1078 if ($status==2) return img_picto($langs->trans('StatusOrderOnProcess'),'statut3');
1079 if ($status==5) return img_picto($langs->trans('StatusOrderToBill'),'statut4');
1080 if ($status==6) return img_picto($langs->trans('StatusOrderOnProcess'),'statut6');
1081 */
1082 print '</td>';
1083 print '</tr>';
1084
1085 $i++;
1086 }
1087
1088 print '<tr class="liste_total"><td colspan="4">'.$langs->trans("Number").': '.$i.'</td>';
1089 print '<td class="right" width="100">'.$langs->trans("TotalHT").' : '.price($total_HT).'</td>';
1090 print '<td class="right" width="100">'.$langs->trans("TotalTTC").' : '.price($total_TTC).'</td>';
1091 print '<td>&nbsp;</td>';
1092 print '</tr>';
1093 } else {
1094 $this->error = $this->db->lasterror();
1095 return -1;
1096 }
1097 }
1098
1099 return 0;
1100 }
1101
1102 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1108 public function fetch_lines()
1109 {
1110 // phpcs:enable
1111 $this->lines = array();
1112
1113 $sql = ' SELECT de.rowid, de.comments, de.qty, de.value_unit, de.date, de.rang,';
1114 $sql .= " de.".$this->fk_element.", de.fk_c_type_fees, de.fk_c_exp_tax_cat, de.fk_projet as fk_project,";
1115 $sql .= ' de.tva_tx, de.vat_src_code,';
1116 $sql .= ' de.localtax1_tx, de.localtax2_tx, de.localtax1_type, de.localtax2_type,';
1117 $sql .= ' de.fk_ecm_files,';
1118 $sql .= ' de.total_ht, de.total_tva, de.total_ttc,';
1119 $sql .= ' de.total_localtax1, de.total_localtax2, de.rule_warning_message,';
1120 $sql .= ' ctf.code as code_type_fees, ctf.label as label_type_fees, ctf.accountancy_code as accountancy_code_type_fees,';
1121 $sql .= ' p.ref as ref_projet, p.title as title_projet';
1122 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line.' as de';
1123 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_type_fees as ctf ON de.fk_c_type_fees = ctf.id';
1124 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'projet as p ON de.fk_projet = p.rowid';
1125 $sql .= " WHERE de.".$this->fk_element." = ".((int) $this->id);
1126 if (getDolGlobalString('EXPENSEREPORT_LINES_SORTED_BY_ROWID')) {
1127 $sql .= ' ORDER BY de.rang ASC, de.rowid ASC';
1128 } else {
1129 $sql .= ' ORDER BY de.rang ASC, de.date ASC';
1130 }
1131
1132 $resql = $this->db->query($sql);
1133 if ($resql) {
1134 $num = $this->db->num_rows($resql);
1135 $i = 0;
1136 while ($i < $num) {
1137 $objp = $this->db->fetch_object($resql);
1138
1139 $deplig = new ExpenseReportLine($this->db);
1140
1141 $deplig->rowid = $objp->rowid;
1142 $deplig->id = $objp->rowid;
1143 $deplig->comments = $objp->comments;
1144 $deplig->qty = $objp->qty;
1145 $deplig->value_unit = $objp->value_unit;
1146 $deplig->date = $objp->date;
1147 $deplig->dates = $this->db->jdate($objp->date);
1148
1149 $deplig->fk_expensereport = $objp->fk_expensereport;
1150 $deplig->fk_c_type_fees = $objp->fk_c_type_fees;
1151 $deplig->fk_c_exp_tax_cat = $objp->fk_c_exp_tax_cat;
1152 $deplig->fk_projet = $objp->fk_project; // deprecated
1153 $deplig->fk_project = $objp->fk_project;
1154 $deplig->fk_ecm_files = $objp->fk_ecm_files;
1155
1156 $deplig->total_ht = $objp->total_ht;
1157 $deplig->total_tva = $objp->total_tva;
1158 $deplig->total_ttc = $objp->total_ttc;
1159 $deplig->total_localtax1 = $objp->total_localtax1;
1160 $deplig->total_localtax2 = $objp->total_localtax2;
1161
1162 $deplig->type_fees_code = empty($objp->code_type_fees) ? 'TF_OTHER' : $objp->code_type_fees;
1163 $deplig->type_fees_libelle = $objp->label_type_fees;
1164 $deplig->type_fees_accountancy_code = $objp->accountancy_code_type_fees;
1165
1166 $deplig->tva_tx = $objp->tva_tx;
1167 $deplig->vatrate = $objp->tva_tx;
1168 $deplig->vat_src_code = $objp->vat_src_code;
1169 $deplig->localtax1_tx = $objp->localtax1_tx;
1170 $deplig->localtax2_tx = $objp->localtax2_tx;
1171 $deplig->localtax1_type = $objp->localtax1_type;
1172 $deplig->localtax2_type = $objp->localtax2_type;
1173
1174 $deplig->projet_ref = $objp->ref_projet;
1175 $deplig->projet_title = $objp->title_projet;
1176
1177 $deplig->rule_warning_message = $objp->rule_warning_message;
1178
1179 $deplig->rang = $objp->rang;
1180
1181 $this->lines[$i] = $deplig;
1182
1183 $i++;
1184 }
1185 $this->db->free($resql);
1186 return 1;
1187 } else {
1188 $this->error = $this->db->lasterror();
1189 dol_syslog(get_class($this)."::fetch_lines: Error ".$this->error, LOG_ERR);
1190 return -3;
1191 }
1192 }
1193
1194
1202 public function delete($user = null, $notrigger = 0)
1203 {
1204 global $conf;
1205 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1206
1207 $error = 0;
1208
1209 $this->db->begin();
1210
1211 if (!$notrigger) {
1212 // Call trigger
1213 $result = $this->call_trigger('EXPENSE_REPORT_DELETE', $user);
1214 if ($result < 0) {
1215 $error++;
1216 }
1217 // End call triggers
1218 }
1219
1220 // Delete extrafields of lines and lines
1221 if (!$error && !empty($this->table_element_line)) {
1222 $tabletodelete = $this->table_element_line;
1223 //$sqlef = "DELETE FROM ".MAIN_DB_PREFIX.$tabletodelete."_extrafields WHERE fk_object IN (SELECT rowid FROM ".MAIN_DB_PREFIX.$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id).")";
1224 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id);
1225 if (!$this->db->query($sql)) {
1226 $error++;
1227 $this->error = $this->db->lasterror();
1228 $this->errors[] = $this->error;
1229 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
1230 }
1231 }
1232
1233 if (!$error) {
1234 // Delete linked object
1235 $res = $this->deleteObjectLinked();
1236 if ($res < 0) {
1237 $error++;
1238 }
1239 }
1240
1241 if (!$error) {
1242 // Delete linked contacts
1243 $res = $this->delete_linked_contact();
1244 if ($res < 0) {
1245 $error++;
1246 }
1247 }
1248
1249 // Removed extrafields of object
1250 if (!$error) {
1251 $result = $this->deleteExtraFields();
1252 if ($result < 0) {
1253 $error++;
1254 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
1255 }
1256 }
1257
1258 // Delete main record
1259 if (!$error) {
1260 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element." WHERE rowid = ".((int) $this->id);
1261 $res = $this->db->query($sql);
1262 if (!$res) {
1263 $error++;
1264 $this->error = $this->db->lasterror();
1265 $this->errors[] = $this->error;
1266 dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
1267 }
1268 }
1269
1270 // Delete record into ECM index and physically
1271 if (!$error) {
1272 $res = $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
1273 $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
1274 if (!$res) {
1275 $error++;
1276 }
1277 }
1278
1279 if (!$error) {
1280 // We remove directory
1281 $ref = dol_sanitizeFileName($this->ref);
1282 if ($conf->expensereport->multidir_output[$this->entity] && !empty($this->ref)) {
1283 $dir = $conf->expensereport->multidir_output[$this->entity]."/".$ref;
1284 $file = $dir."/".$ref.".pdf";
1285 if (file_exists($file)) {
1286 dol_delete_preview($this);
1287
1288 if (!dol_delete_file($file, 0, 0, 0, $this)) {
1289 $this->error = 'ErrorFailToDeleteFile';
1290 $this->errors[] = $this->error;
1291 $this->db->rollback();
1292 return 0;
1293 }
1294 }
1295 if (file_exists($dir)) {
1296 $res = @dol_delete_dir_recursive($dir);
1297 if (!$res) {
1298 $this->error = 'ErrorFailToDeleteDir';
1299 $this->errors[] = $this->error;
1300 $this->db->rollback();
1301 return 0;
1302 }
1303 }
1304 }
1305 }
1306
1307 if (!$error) {
1308 dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
1309 $this->db->commit();
1310 return 1;
1311 } else {
1312 $this->db->rollback();
1313 return -1;
1314 }
1315 }
1316
1324 public function setValidate($fuser, $notrigger = 0)
1325 {
1326 global $conf, $langs, $user;
1327
1328 $error = 0;
1329 $now = dol_now();
1330
1331 // Protection
1332 if ($this->status == self::STATUS_VALIDATED) {
1333 dol_syslog(get_class($this)."::valid action abandoned: already validated", LOG_WARNING);
1334 return 0;
1335 }
1336
1337 $this->date_valid = $now; // Required for the getNextNum later.
1338
1339 // Define new ref
1340 if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
1341 $num = $this->getNextNumRef();
1342 } else {
1343 $num = $this->ref;
1344 }
1345 if (empty($num) || $num < 0) {
1346 return -1;
1347 }
1348
1349 $this->newref = dol_sanitizeFileName($num);
1350
1351 $this->db->begin();
1352
1353 // Validate
1354 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
1355 $sql .= " SET ref = '".$this->db->escape($num)."',";
1356 $sql .= " fk_statut = ".self::STATUS_VALIDATED.",";
1357 $sql .= " date_valid = '".$this->db->idate($this->date_valid)."',";
1358 $sql .= " fk_user_valid = ".((int) $user->id);
1359 $sql .= " WHERE rowid = ".((int) $this->id);
1360
1361 $resql = $this->db->query($sql);
1362 if ($resql) {
1363 if (!$error && !$notrigger) {
1364 // Call trigger
1365 $result = $this->call_trigger('EXPENSE_REPORT_VALIDATE', $fuser);
1366 if ($result < 0) {
1367 $error++;
1368 }
1369 // End call triggers
1370 }
1371
1372 if (!$error) {
1373 $this->oldref = $this->ref;
1374
1375 // Rename directory if dir was a temporary ref
1376 if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1377 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1378
1379 // Now we rename also files into index
1380 $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)."'";
1381 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'expensereport/".$this->db->escape($this->ref)."' AND entity = ".((int) $this->entity);
1382 $resql = $this->db->query($sql);
1383 if (!$resql) {
1384 $error++;
1385 $this->error = $this->db->lasterror();
1386 }
1387 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'expensereport/".$this->db->escape($this->newref)."'";
1388 $sql .= " WHERE filepath = 'expensereport/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1389 $resql = $this->db->query($sql);
1390 if (!$resql) {
1391 $error++;
1392 $this->error = $this->db->lasterror();
1393 }
1394
1395 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1396 $oldref = dol_sanitizeFileName($this->ref);
1397 $newref = dol_sanitizeFileName($num);
1398 $dirsource = $conf->expensereport->multidir_output[$this->entity].'/'.$oldref;
1399 $dirdest = $conf->expensereport->multidir_output[$this->entity].'/'.$newref;
1400 if (!$error && file_exists($dirsource)) {
1401 dol_syslog(get_class($this)."::setValidate() rename dir ".$dirsource." into ".$dirdest);
1402
1403 if (@rename($dirsource, $dirdest)) {
1404 dol_syslog("Rename ok");
1405 // Rename docs starting with $oldref with $newref
1406 $listoffiles = dol_dir_list($dirdest, 'files', 1, '^'.preg_quote($oldref, '/'));
1407 foreach ($listoffiles as $fileentry) {
1408 $dirsource = $fileentry['name'];
1409 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1410 $dirsource = $fileentry['path'].'/'.$dirsource;
1411 $dirdest = $fileentry['path'].'/'.$dirdest;
1412 @rename($dirsource, $dirdest);
1413 }
1414 }
1415 }
1416 }
1417 }
1418
1419 // Set new ref and current status
1420 if (!$error) {
1421 $this->ref = $num;
1423 }
1424
1425 if (empty($error)) {
1426 $this->db->commit();
1427 return 1;
1428 } else {
1429 $this->db->rollback();
1430 $this->error = $this->db->error();
1431 return -2;
1432 }
1433 } else {
1434 $this->db->rollback();
1435 $this->error = $this->db->lasterror();
1436 return -1;
1437 }
1438 }
1439
1440 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1447 public function set_save_from_refuse($fuser)
1448 {
1449 // phpcs:enable
1450 // Sélection de la date de début de la NDF
1451 $sql = 'SELECT date_debut';
1452 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element;
1453 $sql .= " WHERE rowid = ".((int) $this->id);
1454
1455 $result = $this->db->query($sql);
1456
1457 $objp = $this->db->fetch_object($result);
1458
1459 $this->date_debut = $this->db->jdate($objp->date_debut);
1460
1461 if ($this->status != self::STATUS_VALIDATED) {
1462 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1463 $sql .= " SET fk_statut = ".self::STATUS_VALIDATED;
1464 $sql .= " WHERE rowid = ".((int) $this->id);
1465
1466 dol_syslog(get_class($this)."::set_save_from_refuse", LOG_DEBUG);
1467
1468 if ($this->db->query($sql)) {
1469 return 1;
1470 } else {
1471 $this->error = $this->db->lasterror();
1472 return -1;
1473 }
1474 } else {
1475 dol_syslog(get_class($this)."::set_save_from_refuse expensereport already with save status", LOG_WARNING);
1476 }
1477
1478 return 0;
1479 }
1480
1488 public function setApproved($fuser, $notrigger = 0)
1489 {
1490 $now = dol_now();
1491 $error = 0;
1492
1493 // date approval
1494 $this->date_approve = $now;
1495 if ($this->status != self::STATUS_APPROVED) {
1496 $this->db->begin();
1497
1498 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1499 $sql .= " SET ref = '".$this->db->escape($this->ref)."', fk_statut = ".self::STATUS_APPROVED.", fk_user_approve = ".((int) $fuser->id).",";
1500 $sql .= " date_approve='".$this->db->idate($this->date_approve)."'";
1501 $sql .= " WHERE rowid = ".((int) $this->id);
1502 if ($this->db->query($sql)) {
1503 if (!$notrigger) {
1504 // Call trigger
1505 $result = $this->call_trigger('EXPENSE_REPORT_APPROVE', $fuser);
1506
1507 if ($result < 0) {
1508 $error++;
1509 }
1510 // End call triggers
1511 }
1512
1513 if (empty($error)) {
1514 $this->db->commit();
1515 return 1;
1516 } else {
1517 $this->db->rollback();
1518 $this->error = $this->db->error();
1519 return -2;
1520 }
1521 } else {
1522 $this->db->rollback();
1523 $this->error = $this->db->lasterror();
1524 return -1;
1525 }
1526 } else {
1527 dol_syslog(get_class($this)."::setApproved expensereport already with approve status", LOG_WARNING);
1528 }
1529
1530 return 0;
1531 }
1532
1541 public function setDeny($fuser, $details, $notrigger = 0)
1542 {
1543 $now = dol_now();
1544 $error = 0;
1545
1546 // date de refus
1547 if ($this->status != self::STATUS_REFUSED) {
1548 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1549 $sql .= " SET ref = '".$this->db->escape($this->ref)."', fk_statut = ".self::STATUS_REFUSED.", fk_user_refuse = ".((int) $fuser->id).",";
1550 $sql .= " date_refuse='".$this->db->idate($now)."',";
1551 $sql .= " detail_refuse='".$this->db->escape($details)."',";
1552 $sql .= " fk_user_approve = NULL";
1553 $sql .= " WHERE rowid = ".((int) $this->id);
1554 if ($this->db->query($sql)) {
1555 $this->fk_statut = 99; // deprecated
1556 $this->status = 99;
1557 $this->fk_user_refuse = $fuser->id;
1558 $this->detail_refuse = $details;
1559 $this->date_refuse = $now;
1560
1561 if (!$notrigger) {
1562 // Call trigger
1563 $result = $this->call_trigger('EXPENSE_REPORT_DENY', $fuser);
1564
1565 if ($result < 0) {
1566 $error++;
1567 }
1568 // End call triggers
1569 }
1570
1571 if (empty($error)) {
1572 $this->db->commit();
1573 return 1;
1574 } else {
1575 $this->db->rollback();
1576 $this->error = $this->db->error();
1577 return -2;
1578 }
1579 } else {
1580 $this->db->rollback();
1581 $this->error = $this->db->lasterror();
1582 return -1;
1583 }
1584 } else {
1585 dol_syslog(get_class($this)."::setDeny expensereport already with refuse status", LOG_WARNING);
1586 }
1587
1588 return 0;
1589 }
1590
1591 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1601 public function set_unpaid($fuser, $notrigger = 0)
1602 {
1603 // phpcs:enable
1604 dol_syslog(get_class($this)."::set_unpaid is deprecated, use setUnpaid instead", LOG_NOTICE);
1605 return $this->setUnpaid($fuser, $notrigger);
1606 }
1607
1615 public function setUnpaid($fuser, $notrigger = 0)
1616 {
1617 $error = 0;
1618
1619 if ($this->paid) {
1620 $this->db->begin();
1621
1622 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1623 $sql .= " SET paid = 0, fk_statut = ".self::STATUS_APPROVED;
1624 $sql .= " WHERE rowid = ".((int) $this->id);
1625
1626 dol_syslog(get_class($this)."::set_unpaid", LOG_DEBUG);
1627
1628 if ($this->db->query($sql)) {
1629 if (!$notrigger) {
1630 // Call trigger
1631 $result = $this->call_trigger('EXPENSE_REPORT_UNPAID', $fuser);
1632
1633 if ($result < 0) {
1634 $error++;
1635 }
1636 // End call triggers
1637 }
1638
1639 if (empty($error)) {
1640 $this->db->commit();
1641 return 1;
1642 } else {
1643 $this->db->rollback();
1644 $this->error = $this->db->error();
1645 return -2;
1646 }
1647 } else {
1648 $this->db->rollback();
1649 $this->error = $this->db->error();
1650 return -1;
1651 }
1652 } else {
1653 dol_syslog(get_class($this)."::set_unpaid expensereport already with unpaid status", LOG_WARNING);
1654 }
1655
1656 return 0;
1657 }
1658
1659 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1668 public function set_cancel($fuser, $detail, $notrigger = 0)
1669 {
1670 // phpcs:enable
1671 $error = 0;
1672 $this->date_cancel = $this->db->idate(dol_now());
1673 if ($this->status != self::STATUS_CANCELED) {
1674 $this->db->begin();
1675
1676 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1677 $sql .= " SET fk_statut = ".self::STATUS_CANCELED.", fk_user_cancel = ".((int) $fuser->id);
1678 $sql .= ", date_cancel='".$this->db->idate($this->date_cancel)."'";
1679 $sql .= ", detail_cancel='".$this->db->escape($detail)."'";
1680 $sql .= " WHERE rowid = ".((int) $this->id);
1681
1682 dol_syslog(get_class($this)."::set_cancel", LOG_DEBUG);
1683
1684 if ($this->db->query($sql)) {
1685 if (!$notrigger) {
1686 // Call trigger
1687 $result = $this->call_trigger('EXPENSE_REPORT_CANCEL', $fuser);
1688
1689 if ($result < 0) {
1690 $error++;
1691 }
1692 // End call triggers
1693 }
1694
1695 if (empty($error)) {
1696 $this->db->commit();
1697 return 1;
1698 } else {
1699 $this->db->rollback();
1700 $this->error = $this->db->error();
1701 return -2;
1702 }
1703 } else {
1704 $this->db->rollback();
1705 $this->error = $this->db->error();
1706 return -1;
1707 }
1708 } else {
1709 dol_syslog(get_class($this)."::set_cancel expensereport already with cancel status", LOG_WARNING);
1710 }
1711 return 0;
1712 }
1713
1719 public function getNextNumRef()
1720 {
1721 global $langs, $conf;
1722 $langs->load("trips");
1723
1724 if (getDolGlobalString('EXPENSEREPORT_ADDON')) {
1725 $mybool = false;
1726
1727 $file = getDolGlobalString('EXPENSEREPORT_ADDON') . ".php";
1728 $classname = getDolGlobalString('EXPENSEREPORT_ADDON');
1729
1730 // Include file with class
1731 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
1732 foreach ($dirmodels as $reldir) {
1733 $dir = dol_buildpath($reldir."core/modules/expensereport/");
1734
1735 // Load file with numbering class (if found)
1736 $mybool = ((bool) @include_once $dir.$file) || $mybool;
1737 }
1738
1739 if (!$mybool) {
1740 dol_print_error(null, "Failed to include file ".$file);
1741 return '';
1742 }
1743
1744 $obj = new $classname();
1745 '@phan-var-force ModeleNumRefExpenseReport $obj';
1746 $numref = $obj->getNextValue($this);
1747
1748 if ($numref != "") {
1749 return $numref;
1750 } else {
1751 $this->error = $obj->error;
1752 $this->errors = $obj->errors;
1753 //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
1754 return -1;
1755 }
1756 } else {
1757 $this->error = "Error_EXPENSEREPORT_ADDON_NotDefined";
1758 return -2;
1759 }
1760 }
1761
1768 public function getTooltipContentArray($params)
1769 {
1770 global $conf, $langs;
1771
1772 $langs->load('trips');
1773
1774 $nofetch = !empty($params['nofetch']);
1775 $moretitle = $params['moretitle'] ?? '';
1776
1777 $datas = array();
1778 $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("ExpenseReport").'</u>';
1779 if (isset($this->status)) {
1780 $datas['picto'] .= ' '.$this->getLibStatut(5);
1781 }
1782 if ($moretitle) {
1783 $datas['picto'] .= ' - '.$moretitle;
1784 }
1785 if (!empty($this->ref)) {
1786 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1787 }
1788 if (!empty($this->total_ht)) {
1789 $datas['total_ht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
1790 }
1791 if (!empty($this->total_tva)) {
1792 $datas['total_tva'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
1793 }
1794 if (!empty($this->total_ttc)) {
1795 $datas['total_ttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
1796 }
1797
1798 return $datas;
1799 }
1800
1813 public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $moretitle = '', $notooltip = 0, $save_lastsearch_value = -1)
1814 {
1815 global $langs, $hookmanager;
1816
1817 $result = '';
1818
1819 $url = DOL_URL_ROOT.'/expensereport/card.php?id='.$this->id;
1820
1821 if ($short) {
1822 return $url;
1823 }
1824
1825 $params = [
1826 'id' => $this->id,
1827 'objecttype' => $this->element,
1828 'option' => $option,
1829 'moretitle' => $moretitle,
1830 'nofetch' => 1,
1831 ];
1832 $classfortooltip = 'classfortooltip';
1833 $dataparams = '';
1834 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1835 $classfortooltip = 'classforajaxtooltip';
1836 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
1837 $label = '';
1838 } else {
1839 $label = implode($this->getTooltipContentArray($params));
1840 }
1841
1842 if ($option != 'nolink') {
1843 // Add param to save lastsearch_values or not
1844 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1845 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1846 $add_save_lastsearch_values = 1;
1847 }
1848 if ($add_save_lastsearch_values) {
1849 $url .= '&save_lastsearch_values=1';
1850 }
1851 }
1852
1853 $ref = $this->ref;
1854 if (empty($ref)) {
1855 $ref = $this->id;
1856 }
1857
1858 $linkclose = '';
1859 if (empty($notooltip)) {
1860 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1861 $label = $langs->trans("ShowExpenseReport");
1862 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1863 }
1864 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
1865 $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
1866 }
1867
1868 $linkstart = '<a href="'.$url.'"';
1869 $linkstart .= $linkclose.'>';
1870 $linkend = '</a>';
1871
1872 $result .= $linkstart;
1873 if ($withpicto) {
1874 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'"'), 0, 0, $notooltip ? 0 : 1);
1875 }
1876 if ($withpicto != 2) {
1877 $result .= ($max ? dol_trunc($ref, $max) : $ref);
1878 }
1879 $result .= $linkend;
1880
1881 global $action;
1882 $hookmanager->initHooks(array($this->element . 'dao'));
1883 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1884 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1885 if ($reshook > 0) {
1886 $result = $hookmanager->resPrint;
1887 } else {
1888 $result .= $hookmanager->resPrint;
1889 }
1890 return $result;
1891 }
1892
1893 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1901 public function update_totaux_add($ligne_total_ht, $ligne_total_tva)
1902 {
1903 // phpcs:enable
1904 $this->total_ht += (float) $ligne_total_ht;
1905 $this->total_tva += (float) $ligne_total_tva;
1906 $this->total_ttc += $this->total_tva;
1907
1908 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
1909 $sql .= " total_ht = ".$this->total_ht;
1910 $sql .= " , total_ttc = ".$this->total_ttc;
1911 $sql .= " , total_tva = ".$this->total_tva;
1912 $sql .= " WHERE rowid = ".((int) $this->id);
1913
1914 $result = $this->db->query($sql);
1915 if ($result) {
1916 return 1;
1917 } else {
1918 $this->error = $this->db->error();
1919 return -1;
1920 }
1921 }
1922
1938 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)
1939 {
1940 global $langs, $mysoc;
1941
1942 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);
1943
1944 if ($this->status == self::STATUS_DRAFT) {
1945 if (empty($qty)) {
1946 $qty = 0;
1947 }
1948 if (empty($fk_c_type_fees) || $fk_c_type_fees < 0) {
1949 $fk_c_type_fees = 0;
1950 }
1951 if (empty($fk_c_exp_tax_cat) || $fk_c_exp_tax_cat < 0) {
1952 $fk_c_exp_tax_cat = 0;
1953 }
1954 if (empty($vatrate) || $vatrate < 0) {
1955 $vatrate = 0;
1956 }
1957 if (empty($date)) {
1958 $date = '';
1959 }
1960 if (empty($fk_project)) {
1961 $fk_project = 0;
1962 }
1963
1964 $qty = (float) price2num($qty);
1965 if (!preg_match('/\s*\‍((.*)\‍)/', $vatrate)) {
1966 $vatrate = price2num($vatrate); // $txtva can have format '5.0 (XXX)' or '5'
1967 }
1968 $up = price2num($up);
1969
1970 $this->db->begin();
1971
1972 $this->line = new ExpenseReportLine($this->db);
1973
1974 // We don't know seller and buyer for expense reports
1975 $seller = $mysoc; // We use same than current company (expense report are often done in same country)
1976 $seller->tva_assuj = 1; // Most seller uses vat
1977 $buyer = new Societe($this->db);
1978
1979 $localtaxes_type = getLocalTaxesFromRate($vatrate, 0, $buyer, $seller);
1980
1981 $vat_src_code = '';
1982 $reg = array();
1983 if (preg_match('/\s*\‍((.*)\‍)/', $vatrate, $reg)) {
1984 $vat_src_code = $reg[1];
1985 $vatrate = preg_replace('/\s*\‍(.*\‍)/', '', $vatrate); // Remove code into vatrate.
1986 }
1987 $vatrate = preg_replace('/\*/', '', $vatrate);
1988
1989 $tmp = calcul_price_total($qty, $up, 0, (float) price2num($vatrate), -1, -1, 0, 'TTC', 0, $type, $seller, $localtaxes_type);
1990
1991 $this->line->value_unit = $up;
1992
1993 $this->line->vat_src_code = $vat_src_code;
1994 $this->line->vatrate = price2num($vatrate);
1995 $this->line->localtax1_tx = $localtaxes_type[1];
1996 $this->line->localtax2_tx = $localtaxes_type[3];
1997 $this->line->localtax1_type = $localtaxes_type[0];
1998 $this->line->localtax2_type = $localtaxes_type[2];
1999
2000 $this->line->total_ttc = $tmp[2];
2001 $this->line->total_ht = $tmp[0];
2002 $this->line->total_tva = $tmp[1];
2003 $this->line->total_localtax1 = $tmp[9];
2004 $this->line->total_localtax2 = $tmp[10];
2005
2006 $this->line->fk_expensereport = $this->id;
2007 $this->line->qty = $qty;
2008 $this->line->date = $date;
2009 $this->line->fk_c_type_fees = $fk_c_type_fees;
2010 $this->line->fk_c_exp_tax_cat = $fk_c_exp_tax_cat;
2011 $this->line->comments = $comments;
2012 $this->line->fk_projet = $fk_project; // deprecated
2013 $this->line->fk_project = $fk_project;
2014
2015 $this->line->fk_ecm_files = $fk_ecm_files;
2016
2017 $this->applyOffset();
2018 $this->checkRules($type, $seller);
2019
2020 $result = $this->line->insert(0, true);
2021 if ($result > 0) {
2022 $result = $this->update_price(1); // This method is designed to add line from user input so total calculation must be done using 'auto' mode.
2023 if ($result > 0) {
2024 $this->db->commit();
2025 return $this->line->id;
2026 } else {
2027 $this->db->rollback();
2028 return -1;
2029 }
2030 } else {
2031 $this->error = $this->line->error;
2032 dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
2033 $this->db->rollback();
2034 return -2;
2035 }
2036 } else {
2037 dol_syslog(get_class($this)."::addline status of expense report must be Draft to allow use of ->addline()", LOG_ERR);
2038 $this->error = 'ErrorExpenseNotDraft';
2039 return -3;
2040 }
2041 }
2042
2050 public function checkRules($type = 0, $seller = '')
2051 {
2052 global $conf, $db, $langs, $mysoc;
2053
2054 $langs->load('trips');
2055
2056 // We don't know seller and buyer for expense reports
2057 if (!is_object($seller)) {
2058 $seller = $mysoc; // We use same than current company (expense report are often done in same country)
2059 $seller->tva_assuj = 1; // Most seller uses vat
2060 }
2061
2062 $expensereportrule = new ExpenseReportRule($db);
2063 $rulestocheck = $expensereportrule->getAllRule($this->line->fk_c_type_fees, $this->line->date, $this->fk_user_author);
2064
2065 $violation = 0;
2066 $rule_warning_message_tab = array();
2067
2068 $current_total_ttc = $this->line->total_ttc;
2069 $new_current_total_ttc = $this->line->total_ttc;
2070
2071 // check if one is violated
2072 foreach ($rulestocheck as $rule) {
2073 if (in_array($rule->code_expense_rules_type, array('EX_DAY', 'EX_MON', 'EX_YEA'))) {
2074 $amount_to_test = $this->line->getExpAmount($rule, $this->fk_user_author, $rule->code_expense_rules_type);
2075 } else {
2076 $amount_to_test = $current_total_ttc; // EX_EXP
2077 }
2078
2079 $amount_to_test = $amount_to_test - $current_total_ttc + $new_current_total_ttc; // if amount as been modified by a previous rule
2080
2081 if ($amount_to_test > $rule->amount) {
2082 $violation++;
2083
2084 if ($rule->restrictive) {
2085 $this->error = 'ExpenseReportConstraintViolationError';
2086 $this->errors[] = $this->error;
2087
2088 $new_current_total_ttc -= $amount_to_test - $rule->amount; // ex, entered 16€, limit 12€, subtracts 4€;
2089 $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));
2090 } else {
2091 $this->error = 'ExpenseReportConstraintViolationWarning';
2092 $this->errors[] = $this->error;
2093
2094 $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));
2095 }
2096
2097 // No break, we should test if another rule is violated
2098 }
2099 }
2100
2101 $this->line->rule_warning_message = implode('\n', $rule_warning_message_tab);
2102
2103 if ($violation > 0) {
2104 $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);
2105
2106 $this->line->value_unit = $tmp[5];
2107 $this->line->total_ttc = $tmp[2];
2108 $this->line->total_ht = $tmp[0];
2109 $this->line->total_tva = $tmp[1];
2110 $this->line->total_localtax1 = $tmp[9];
2111 $this->line->total_localtax2 = $tmp[10];
2112
2113 return false;
2114 } else {
2115 return true;
2116 }
2117 }
2118
2126 public function applyOffset($type = 0, $seller = '')
2127 {
2128 global $mysoc;
2129
2130 if (!getDolGlobalString('MAIN_USE_EXPENSE_IK')) {
2131 return false;
2132 }
2133
2134 $userauthor = new User($this->db);
2135 if ($userauthor->fetch($this->fk_user_author) <= 0) {
2136 $this->error = 'ErrorCantFetchUser';
2137 $this->errors[] = 'ErrorCantFetchUser';
2138 return false;
2139 }
2140
2141 // We don't know seller and buyer for expense reports
2142 if (!is_object($seller)) {
2143 $seller = $mysoc; // We use same than current company (expense report are often done in same country)
2144 $seller->tva_assuj = 1; // Most seller uses vat
2145 }
2146
2147 $expenseik = new ExpenseReportIk($this->db);
2148 $range = $expenseik->getRangeByUser($userauthor, $this->line->fk_c_exp_tax_cat);
2149
2150 if (empty($range)) {
2151 $this->error = 'ErrorNoRangeAvailable';
2152 $this->errors[] = 'ErrorNoRangeAvailable';
2153 return false;
2154 }
2155
2156 if (getDolGlobalString('MAIN_EXPENSE_APPLY_ENTIRE_OFFSET')) {
2157 $ikoffset = $range->ikoffset;
2158 } else {
2159 $ikoffset = $range->ikoffset / 12; // The amount of offset is a global value for the year
2160 }
2161
2162 // Test if ikoffset has been applied for the current month
2163 if (!$this->offsetAlreadyGiven()) {
2164 $new_up = $range->coef + ($ikoffset / $this->line->qty);
2165 $tmp = calcul_price_total($this->line->qty, $new_up, 0, $this->line->vatrate, 0, 0, 0, 'TTC', 0, $type, $seller);
2166
2167 $this->line->value_unit = $tmp[5];
2168 $this->line->total_ttc = $tmp[2];
2169 $this->line->total_ht = $tmp[0];
2170 $this->line->total_tva = $tmp[1];
2171 $this->line->total_localtax1 = $tmp[9];
2172 $this->line->total_localtax2 = $tmp[10];
2173
2174 return true;
2175 }
2176
2177 return false;
2178 }
2179
2185 public function offsetAlreadyGiven()
2186 {
2187 $sql = 'SELECT e.rowid FROM '.MAIN_DB_PREFIX.'expensereport e';
2188 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."expensereport_det d ON (e.rowid = d.fk_expensereport)";
2189 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."c_type_fees f ON (d.fk_c_type_fees = f.id AND f.code = 'EX_KME')";
2190 $sql .= " WHERE e.fk_user_author = ".(int) $this->fk_user_author;
2191 $sql .= " AND YEAR(d.date) = '".dol_print_date($this->line->date, '%Y')."' AND MONTH(d.date) = '".dol_print_date($this->line->date, '%m')."'";
2192 if (!empty($this->line->id)) {
2193 $sql .= ' AND d.rowid <> '.((int) $this->line->id);
2194 }
2195
2196 dol_syslog(get_class($this)."::offsetAlreadyGiven");
2197 $resql = $this->db->query($sql);
2198 if ($resql) {
2199 $num = $this->db->num_rows($resql);
2200 if ($num > 0) {
2201 return true;
2202 }
2203 } else {
2204 dol_print_error($this->db);
2205 }
2206
2207 return false;
2208 }
2209
2227 public function updateline($rowid, $type_fees_id, $projet_id, $vatrate, $comments, $qty, $value_unit, $date, $expensereport_id, $fk_c_exp_tax_cat = 0, $fk_ecm_files = 0, $notrigger = 0)
2228 {
2229 global $user, $mysoc;
2230
2231 if ($this->status == self::STATUS_DRAFT || $this->status == self::STATUS_REFUSED) {
2232 $this->db->begin();
2233
2234 $error = 0;
2235 $type = 0; // TODO What if type is service ?
2236
2237 // We don't know seller and buyer for expense reports
2238 $seller = $mysoc; // We use same than current company (expense report are often done in same country)
2239 $seller->tva_assuj = 1; // Most seller uses vat
2240 $seller->localtax1_assuj = $mysoc->localtax1_assuj; // We don't know, we reuse the state of company
2241 $seller->localtax2_assuj = $mysoc->localtax1_assuj; // We don't know, we reuse the state of company
2242 $buyer = new Societe($this->db);
2243
2244 $localtaxes_type = getLocalTaxesFromRate($vatrate, 0, $buyer, $seller);
2245
2246 // Clean vat code
2247 $reg = array();
2248 $vat_src_code = '';
2249 if (preg_match('/\‍((.*)\‍)/', (string) $vatrate, $reg)) {
2250 $vat_src_code = $reg[1];
2251 $vatrate = preg_replace('/\s*\‍(.*\‍)/', '', (string) $vatrate); // Remove code into vatrate.
2252 }
2253 $vatrate = preg_replace('/\*/', '', $vatrate);
2254
2255 $tmp = calcul_price_total($qty, $value_unit, 0, (float) price2num($vatrate), -1, -1, 0, 'TTC', 0, $type, $seller, $localtaxes_type);
2256 // calcul total of line
2257 // $total_ttc = price2num($qty*$value_unit, 'MT');
2258
2259 $tx_tva = 1 + (float) $vatrate / 100;
2260
2261 $this->line = new ExpenseReportLine($this->db);
2262 $this->line->comments = $comments;
2263 $this->line->qty = $qty;
2264 $this->line->value_unit = $value_unit;
2265 $this->line->date = $date;
2266
2267 $this->line->fk_expensereport = $expensereport_id;
2268 $this->line->fk_c_type_fees = $type_fees_id;
2269 $this->line->fk_c_exp_tax_cat = $fk_c_exp_tax_cat;
2270 $this->line->fk_projet = $projet_id; // deprecated
2271 $this->line->fk_project = $projet_id;
2272
2273 $this->line->vat_src_code = $vat_src_code;
2274 $this->line->vatrate = price2num($vatrate);
2275 $this->line->localtax1_tx = $localtaxes_type[1];
2276 $this->line->localtax2_tx = $localtaxes_type[3];
2277 $this->line->localtax1_type = $localtaxes_type[0];
2278 $this->line->localtax2_type = $localtaxes_type[2];
2279
2280 $this->line->total_ttc = $tmp[2];
2281 $this->line->total_ht = $tmp[0];
2282 $this->line->total_tva = $tmp[1];
2283 $this->line->total_localtax1 = $tmp[9];
2284 $this->line->total_localtax2 = $tmp[10];
2285
2286 $this->line->fk_ecm_files = $fk_ecm_files;
2287
2288 $this->line->id = ((int) $rowid);
2289
2290 // Select des infos sur le type fees
2291 $sql = "SELECT c.code as code_type_fees, c.label as label_type_fees";
2292 $sql .= " FROM ".MAIN_DB_PREFIX."c_type_fees as c";
2293 $sql .= " WHERE c.id = ".((int) $type_fees_id);
2294 $resql = $this->db->query($sql);
2295 if ($resql) {
2296 $objp_fees = $this->db->fetch_object($resql);
2297 $this->line->type_fees_code = $objp_fees->code_type_fees;
2298 $this->line->type_fees_libelle = $objp_fees->label_type_fees;
2299 $this->db->free($resql);
2300 }
2301
2302 // Select des information du projet
2303 $sql = "SELECT p.ref as ref_projet, p.title as title_projet";
2304 $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
2305 $sql .= " WHERE p.rowid = ".((int) $projet_id);
2306 $resql = $this->db->query($sql);
2307 if ($resql) {
2308 $objp_projet = $this->db->fetch_object($resql);
2309 $this->line->projet_ref = $objp_projet->ref_projet;
2310 $this->line->projet_title = $objp_projet->title_projet;
2311 $this->db->free($resql);
2312 }
2313
2314 $this->applyOffset();
2315 $this->checkRules();
2316
2317 $result = $this->line->update($user);
2318 if ($result < 0) {
2319 $error++;
2320 }
2321
2322 if (!$error && !$notrigger) {
2323 // Call triggers
2324 $result = $this->call_trigger('EXPENSE_REPORT_DET_MODIFY', $user);
2325 if ($result < 0) {
2326 $error++;
2327 }
2328 // End call triggers
2329 }
2330
2331 if (!$error) {
2332 $this->db->commit();
2333 return 1;
2334 } else {
2335 $this->error = $this->line->error;
2336 $this->errors = $this->line->errors;
2337 $this->db->rollback();
2338 return -2;
2339 }
2340 }
2341
2342 return 0;
2343 }
2344
2353 public function deleteLine($rowid, $fuser = '', $notrigger = 0)
2354 {
2355 $error = 0;
2356
2357 $this->db->begin();
2358
2359 if (!$notrigger) {
2360 // Call triggers
2361 $result = $this->call_trigger('EXPENSE_REPORT_DET_DELETE', $fuser);
2362 if ($result < 0) {
2363 $error++;
2364 }
2365 // End call triggers
2366 }
2367
2368 $sql = ' DELETE FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2369 $sql .= ' WHERE rowid = '.((int) $rowid);
2370
2371 dol_syslog(get_class($this)."::deleteline sql=".$sql);
2372 $result = $this->db->query($sql);
2373
2374 if (!$result || $error > 0) {
2375 $this->error = $this->db->error();
2376 dol_syslog(get_class($this)."::deleteline Error ".$this->error, LOG_ERR);
2377 $this->db->rollback();
2378 return -1;
2379 }
2380
2381 $this->update_price(1);
2382
2383 $this->db->commit();
2384
2385 return 1;
2386 }
2387
2388 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2397 public function periode_existe($fuser, $date_debut, $date_fin)
2398 {
2399 global $conf;
2400
2401 // phpcs:enable
2402 $sql = "SELECT rowid, date_debut, date_fin";
2403 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element;
2404 $sql .= " WHERE entity = ".((int) $conf->entity); // not shared, only for the current entity
2405 $sql .= " AND fk_user_author = ".((int) $fuser->id);
2406
2407 dol_syslog(get_class($this)."::periode_existe sql=".$sql);
2408 $result = $this->db->query($sql);
2409 if ($result) {
2410 $num_rows = $this->db->num_rows($result);
2411 $i = 0;
2412
2413 if ($num_rows > 0) {
2414 $date_d_form = $date_debut;
2415 $date_f_form = $date_fin;
2416
2417 while ($i < $num_rows) {
2418 $objp = $this->db->fetch_object($result);
2419
2420 $date_d_req = $this->db->jdate($objp->date_debut); // 3
2421 $date_f_req = $this->db->jdate($objp->date_fin); // 4
2422
2423 if (!($date_f_form < $date_d_req || $date_d_form > $date_f_req)) {
2424 return $objp->rowid;
2425 }
2426
2427 $i++;
2428 }
2429
2430 return 0;
2431 } else {
2432 return 0;
2433 }
2434 } else {
2435 $this->error = $this->db->lasterror();
2436 dol_syslog(get_class($this)."::periode_existe Error ".$this->error, LOG_ERR);
2437 return -1;
2438 }
2439 }
2440
2441
2442 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2450 {
2451 // phpcs:enable
2452 $users_validator = array();
2453
2454 $sql = "SELECT DISTINCT ur.fk_user";
2455 $sql .= " FROM ".MAIN_DB_PREFIX."user_rights as ur, ".MAIN_DB_PREFIX."rights_def as rd";
2456 $sql .= " WHERE ur.fk_id = rd.id and rd.module = 'expensereport' AND rd.perms = 'approve'"; // Permission 'Approve';
2457 $sql .= " UNION";
2458 $sql .= " SELECT DISTINCT ugu.fk_user";
2459 $sql .= " FROM ".MAIN_DB_PREFIX."usergroup_user as ugu, ".MAIN_DB_PREFIX."usergroup_rights as ur, ".MAIN_DB_PREFIX."rights_def as rd";
2460 $sql .= " WHERE ugu.fk_usergroup = ur.fk_usergroup AND ur.fk_id = rd.id and rd.module = 'expensereport' AND rd.perms = 'approve'"; // Permission 'Approve';
2461 //print $sql;
2462
2463 dol_syslog(get_class($this)."::fetch_users_approver_expensereport sql=".$sql);
2464 $result = $this->db->query($sql);
2465 if ($result) {
2466 $num_rows = $this->db->num_rows($result);
2467 $i = 0;
2468 while ($i < $num_rows) {
2469 $objp = $this->db->fetch_object($result);
2470 array_push($users_validator, $objp->fk_user);
2471 $i++;
2472 }
2473 return $users_validator;
2474 } else {
2475 $this->error = $this->db->lasterror();
2476 dol_syslog(get_class($this)."::fetch_users_approver_expensereport Error ".$this->error, LOG_ERR);
2477 return -1;
2478 }
2479 }
2480
2492 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2493 {
2494 $outputlangs->load("trips");
2495
2496 if (!dol_strlen($modele)) {
2497 if (!empty($this->model_pdf)) {
2498 $modele = $this->model_pdf;
2499 } elseif (getDolGlobalString('EXPENSEREPORT_ADDON_PDF')) {
2500 $modele = getDolGlobalString('EXPENSEREPORT_ADDON_PDF');
2501 }
2502 }
2503
2504 if (!empty($modele)) {
2505 $modelpath = "core/modules/expensereport/doc/";
2506
2507 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2508 } else {
2509 return 0;
2510 }
2511 }
2512
2519 public function listOfTypes($active = 1)
2520 {
2521 global $langs;
2522 $ret = array();
2523 $sql = "SELECT id, code, label";
2524 $sql .= " FROM ".MAIN_DB_PREFIX."c_type_fees";
2525 $sql .= " WHERE active = ".((int) $active);
2526 dol_syslog(get_class($this)."::listOfTypes", LOG_DEBUG);
2527 $result = $this->db->query($sql);
2528 if ($result) {
2529 $num = $this->db->num_rows($result);
2530 $i = 0;
2531 while ($i < $num) {
2532 $obj = $this->db->fetch_object($result);
2533 $ret[$obj->code] = (($langs->transnoentitiesnoconv($obj->code) != $obj->code) ? $langs->transnoentitiesnoconv($obj->code) : $obj->label);
2534 $i++;
2535 }
2536 } else {
2537 dol_print_error($this->db);
2538 }
2539 return $ret;
2540 }
2541
2547 public function loadStateBoard()
2548 {
2549 global $user;
2550
2551 $this->nb = array();
2552
2553 $sql = "SELECT count(ex.rowid) as nb";
2554 $sql .= " FROM ".MAIN_DB_PREFIX."expensereport as ex";
2555 $sql .= " WHERE ex.fk_statut > 0";
2556 $sql .= " AND ex.entity IN (".getEntity('expensereport').")";
2557 if (!$user->hasRight('expensereport', 'readall')) {
2558 $userchildids = $user->getAllChildIds(1);
2559 $sql .= " AND (ex.fk_user_author IN (".$this->db->sanitize(implode(',', $userchildids)).")";
2560 $sql .= " OR ex.fk_user_validator IN (".$this->db->sanitize(implode(',', $userchildids))."))";
2561 }
2562
2563 $resql = $this->db->query($sql);
2564 if ($resql) {
2565 while ($obj = $this->db->fetch_object($resql)) {
2566 $this->nb["expensereports"] = $obj->nb;
2567 }
2568 $this->db->free($resql);
2569 return 1;
2570 } else {
2571 dol_print_error($this->db);
2572 $this->error = $this->db->error();
2573 return -1;
2574 }
2575 }
2576
2577 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2585 public function load_board($user, $option = 'topay')
2586 {
2587 // phpcs:enable
2588 global $conf, $langs;
2589
2590 if ($user->socid) {
2591 return -1; // protection pour eviter appel par utilisateur externe
2592 }
2593
2594 $now = dol_now();
2595
2596 $sql = "SELECT ex.rowid, ex.date_valid";
2597 $sql .= " FROM ".MAIN_DB_PREFIX."expensereport as ex";
2598 if ($option == 'toapprove') {
2599 $sql .= " WHERE ex.fk_statut = ".self::STATUS_VALIDATED;
2600 } else {
2601 $sql .= " WHERE ex.fk_statut = ".self::STATUS_APPROVED;
2602 }
2603 $sql .= " AND ex.entity IN (".getEntity('expensereport').")";
2604 if (!$user->hasRight('expensereport', 'readall')) {
2605 $userchildids = $user->getAllChildIds(1);
2606 $sql .= " AND (ex.fk_user_author IN (".$this->db->sanitize(implode(',', $userchildids)).")";
2607 $sql .= " OR ex.fk_user_validator IN (".$this->db->sanitize(implode(',', $userchildids))."))";
2608 }
2609
2610 $resql = $this->db->query($sql);
2611 if ($resql) {
2612 $langs->load("trips");
2613
2614 $response = new WorkboardResponse();
2615 if ($option == 'toapprove') {
2616 $response->warning_delay = $conf->expensereport->approve->warning_delay / 60 / 60 / 24;
2617 $response->label = $langs->trans("ExpenseReportsToApprove");
2618 $response->labelShort = $langs->trans("ToApprove");
2619 $response->url = DOL_URL_ROOT.'/expensereport/list.php?mainmenu=hrm&amp;statut='.self::STATUS_VALIDATED;
2620 } else {
2621 $response->warning_delay = $conf->expensereport->payment->warning_delay / 60 / 60 / 24;
2622 $response->label = $langs->trans("ExpenseReportsToPay");
2623 $response->labelShort = $langs->trans("StatusToPay");
2624 $response->url = DOL_URL_ROOT.'/expensereport/list.php?mainmenu=hrm&amp;statut='.self::STATUS_APPROVED;
2625 }
2626 $response->img = img_object('', "trip");
2627
2628 while ($obj = $this->db->fetch_object($resql)) {
2629 $response->nbtodo++;
2630
2631 if ($option == 'toapprove') {
2632 if ($this->db->jdate($obj->date_valid) < ($now - $conf->expensereport->approve->warning_delay)) {
2633 $response->nbtodolate++;
2634 }
2635 } else {
2636 if ($this->db->jdate($obj->date_valid) < ($now - $conf->expensereport->payment->warning_delay)) {
2637 $response->nbtodolate++;
2638 }
2639 }
2640 }
2641
2642 return $response;
2643 } else {
2644 dol_print_error($this->db);
2645 $this->error = $this->db->error();
2646 return -1;
2647 }
2648 }
2649
2656 public function hasDelay($option)
2657 {
2658 global $conf;
2659
2660 // Only valid expenses reports
2661 if ($option == 'toapprove' && $this->status != 2) {
2662 return false;
2663 }
2664 if ($option == 'topay' && $this->status != 5) {
2665 return false;
2666 }
2667
2668 $now = dol_now();
2669 if ($option == 'toapprove') {
2670 return (!empty($this->datevalid) ? $this->datevalid : $this->date_valid) < ($now - $conf->expensereport->approve->warning_delay);
2671 } else {
2672 return (!empty($this->datevalid) ? $this->datevalid : $this->date_valid) < ($now - $conf->expensereport->payment->warning_delay);
2673 }
2674 }
2675
2681 public function getVentilExportCompta()
2682 {
2683 $alreadydispatched = 0;
2684
2685 $type = 'expense_report';
2686
2687 $sql = " SELECT COUNT(ab.rowid) as nb FROM ".MAIN_DB_PREFIX."accounting_bookkeeping as ab WHERE ab.doc_type='".$this->db->escape($type)."' AND ab.fk_doc = ".((int) $this->id);
2688 $resql = $this->db->query($sql);
2689 if ($resql) {
2690 $obj = $this->db->fetch_object($resql);
2691 if ($obj) {
2692 $alreadydispatched = $obj->nb;
2693 }
2694 } else {
2695 $this->error = $this->db->lasterror();
2696 return -1;
2697 }
2698
2699 if ($alreadydispatched) {
2700 return 1;
2701 }
2702 return 0;
2703 }
2704
2710 public function getSumPayments()
2711 {
2712 $table = 'payment_expensereport';
2713 $field = 'fk_expensereport';
2714
2715 $sql = 'SELECT sum(amount) as amount';
2716 $sql .= ' FROM '.MAIN_DB_PREFIX.$table;
2717 $sql .= " WHERE ".$field." = ".((int) $this->id);
2718
2719 dol_syslog(get_class($this)."::getSumPayments", LOG_DEBUG);
2720 $resql = $this->db->query($sql);
2721 if ($resql) {
2722 $obj = $this->db->fetch_object($resql);
2723 $this->db->free($resql);
2724 return (empty($obj->amount) ? 0 : $obj->amount);
2725 } else {
2726 $this->error = $this->db->lasterror();
2727 return -1;
2728 }
2729 }
2730
2739 public function computeTotalKm($fk_cat, $qty, $tva)
2740 {
2741 global $langs, $db, $conf;
2742
2743 $cumulYearQty = 0;
2744 $ranges = array();
2745 $coef = 0;
2746
2747
2748 if ($fk_cat < 0) {
2749 $this->error = $langs->trans('ErrorBadParameterCat');
2750 return -1;
2751 }
2752
2753 if ($qty <= 0) {
2754 $this->error = $langs->trans('ErrorBadParameterQty');
2755 return -1;
2756 }
2757
2758 $currentUser = new User($db);
2759 $currentUser->fetch($this->fk_user);
2760 $currentUser->loadRights('expensereport');
2761 //Clean
2762 $qty = (float) price2num($qty);
2763
2764 $sql = " SELECT r.range_ik, t.ikoffset, t.coef";
2765 $sql .= " FROM ".MAIN_DB_PREFIX."expensereport_ik t";
2766 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_exp_tax_range r ON r.rowid = t.fk_range";
2767 $sql .= " WHERE t.fk_c_exp_tax_cat = ".(int) $fk_cat;
2768 $sql .= " ORDER BY r.range_ik ASC";
2769
2770 dol_syslog("expenseReport::computeTotalkm sql=".$sql, LOG_DEBUG);
2771
2772 $result = $this->db->query($sql);
2773
2774 if ($result) {
2775 if ($conf->global->EXPENSEREPORT_CALCULATE_MILEAGE_EXPENSE_COEFFICIENT_ON_CURRENT_YEAR) {
2776 $arrayDate = dol_getdate(dol_now());
2777 $sql = " SELECT count(n.qty) as cumul FROM ".MAIN_DB_PREFIX."expensereport_det n";
2778 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."expensereport e ON e.rowid = n.fk_expensereport";
2779 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_type_fees tf ON tf.id = n.fk_c_type_fees";
2780 $sql .= " WHERE e.fk_user_author = ".(int) $this->fk_user_author;
2781 $sql .= " AND YEAR(n.date) = ".(int) $arrayDate['year'];
2782 $sql .= " AND tf.code = 'EX_KME' ";
2783 $sql .= " AND e.fk_statut = ".(int) ExpenseReport::STATUS_VALIDATED;
2784
2785 $resql = $this->db->query($sql);
2786
2787 if ($resql) {
2788 $obj = $this->db->fetch_object($resql);
2789 $cumulYearQty = $obj->cumul;
2790 }
2791
2792 $qty += (float) $cumulYearQty;
2793 }
2794
2795 $num = $this->db->num_rows($result);
2796
2797 if ($num) {
2798 for ($i = 0; $i < $num; $i++) {
2799 $obj = $this->db->fetch_object($result);
2800
2801 $ranges[$i] = $obj;
2802 }
2803 '@phan-var-force Object[] $ranges';
2804
2805 for ($i = 0; $i < $num; $i++) {
2806 if ($i < ($num - 1)) {
2807 // @phan-suppress-next-line PhanTypeInvalidDimOffset
2808 if ($qty > $ranges[$i]->range_ik && $qty < $ranges[$i + 1]->range_ik) {
2809 $coef = $ranges[$i]->coef;
2810 $offset = $ranges[$i]->ikoffset;
2811 }
2812 } else {
2813 if ($qty > $ranges[$i]->range_ik) {
2814 $coef = $ranges[$i]->coef;
2815 $offset = $ranges[$i]->ikoffset;
2816 }
2817 }
2818 }
2819 $total_ht = $coef;
2820 return $total_ht;
2821 } else {
2822 $this->error = $langs->trans('TaxUndefinedForThisCategory');
2823 return 0;
2824 }
2825 } else {
2826 $this->error = $this->db->error()." sql=".$sql;
2827
2828 return -1;
2829 }
2830 }
2831
2839 public function getKanbanView($option = '', $arraydata = null)
2840 {
2841 global $langs;
2842
2843 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2844
2845 $return = '<div class="box-flex-item box-flex-grow-zero">';
2846 $return .= '<div class="info-box info-box-sm">';
2847 $return .= '<span class="info-box-icon bg-infobox-action">';
2848 $return .= img_picto('', $this->picto);
2849 $return .= '</span>';
2850 $return .= '<div class="info-box-content">';
2851 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref).'</span>';
2852 if ($selected >= 0) {
2853 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
2854 }
2855 if (array_key_exists('userauthor', $arraydata) && $arraydata['userauthor'] instanceof User) {
2856 $return .= '<br><span class="info-box-label">'.$arraydata['userauthor']->getNomUrl(-1).'</span>';
2857 }
2858 if (property_exists($this, 'date_debut') && property_exists($this, 'date_fin')) {
2859 $return .= '<br><span class="info-box-label">'.dol_print_date($this->date_debut, 'day').'</span>';
2860 $return .= ' <span class="opacitymedium">'.$langs->trans("To").'</span> ';
2861 $return .= '<span class="info-box-label">'.dol_print_date($this->date_fin, 'day').'</span>';
2862 }
2863 if (method_exists($this, 'getLibStatut')) {
2864 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
2865 }
2866 $return .= '</div>';
2867 $return .= '</div>';
2868 $return .= '</div>';
2869 return $return;
2870 }
2871}
print $langs trans("AuditedSecurityEvents").'</strong >< span class="opacitymedium"></span >< br > status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition security.php:640
$object ref
Definition info.php:79
Parent class of all other business classes (invoices, contracts, proposals, orders,...
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
deleteEcmFiles($mode=0)
Delete related files of object in database.
update_price($exclspec=0, $roundingadjust='auto', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid=0, $f_user=null, $notrigger=0)
Delete all links between an object $this.
setErrorsFromObject($object)
setErrorsFromObject
deleteExtraFields()
Delete all extra fields values for the current object.
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
delete_linked_contact($source='', $code='')
Delete all links between an object $this and all its contacts in llx_element_contact.
call_trigger($triggerName, $user)
Call trigger based on this instance.
Class to manage Trips and Expenses.
setPaid($id, $fuser, $notrigger=0)
Classify the expense report as paid.
loadStateBoard()
Load the indicators this->nb for the state board.
__construct($db)
Constructor.
checkRules($type=0, $seller='')
Check constraint of rules and update price if needed.
updateline($rowid, $type_fees_id, $projet_id, $vatrate, $comments, $qty, $value_unit, $date, $expensereport_id, $fk_c_exp_tax_cat=0, $fk_ecm_files=0, $notrigger=0)
Update an expense report line.
getNextNumRef()
Return next reference of expense report not already used.
createFromClone(User $user, $fk_user_author)
Load an object from its id and create a new one in database.
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)
Add expense report line.
const STATUS_DRAFT
Draft status.
computeTotalKm($fk_cat, $qty, $tva)
Compute the cost of the kilometers expense based on the number of kilometers and the vehicle category...
offsetAlreadyGiven()
If the sql find any rows then the ikoffset is already given (ikoffset is applied at the first expense...
listOfTypes($active=1)
List of types.
const STATUS_APPROVED
Classified approved.
set_save_from_refuse($fuser)
set_save_from_refuse
periode_existe($fuser, $date_debut, $date_fin)
periode_existe
setValidate($fuser, $notrigger=0)
Set to status validate.
getSumPayments()
Return amount of payments already done.
getLibStatut($mode=0)
Returns the label status.
set_cancel($fuser, $detail, $notrigger=0)
set_cancel
getNomUrl($withpicto=0, $option='', $max=0, $short=0, $moretitle='', $notooltip=0, $save_lastsearch_value=-1)
Return clickable name (with picto eventually)
set_unpaid($fuser, $notrigger=0)
set_unpaid
info($id)
Load information on object.
getTooltipContentArray($params)
getTooltipContentArray
hasDelay($option)
Return if an expense report is late or not.
applyOffset($type=0, $seller='')
Method to apply the offset if needed.
const STATUS_CANCELED
Classified canceled.
const STATUS_CLOSED
Classified paid.
const STATUS_REFUSED
Classified refused.
deleteLine($rowid, $fuser='', $notrigger=0)
deleteline
setDeny($fuser, $details, $notrigger=0)
setDeny
getVentilExportCompta()
Return if object was dispatched into bookkeeping.
getKanbanView($option='', $arraydata=null)
Return clickable link of object (with optional picto)
update($user, $notrigger=0, $userofexpensereport=null)
update
const STATUS_VALIDATED
Validated (need to be paid)
create($user, $notrigger=0)
Create object in database.
load_board($user, $option='topay')
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
set_paid($id, $fuser, $notrigger=0)
Classify the expense report as paid.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template module.
initAsSpecimen()
Initialise an instance with random values.
fetch_users_approver_expensereport()
Return list of people with permission to validate expense reports.
setApproved($fuser, $notrigger=0)
Set status to approved.
LibStatut($status, $mode=0)
Returns the label of a status.
setUnpaid($fuser, $notrigger=0)
set_unpaid
fetch_line_by_project($projectid, $user)
fetch_line_by_project
update_totaux_add($ligne_total_ht, $ligne_total_tva)
Update total of an expense report when you add a line.
Class to manage inventories.
Class of expense report details lines.
Class to manage inventories.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage Dolibarr users.
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null, $allowdotdot=false, $indexdatabase=1, $nolog=0)
Remove a file or several files with a mask.
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:63
dol_delete_preview($object)
Delete all preview files linked to object instance.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dolGetFirstLastname($firstname, $lastname, $nameorder=-1)
Return firstname and lastname in correct order.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_getdate($timestamp, $fast=false, $forcetimezone='')
Return an array with locale date info.
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller='', $localtaxes_array=[], $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition price.lib.php:88