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 
30 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
31 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
32 require_once DOL_DOCUMENT_ROOT.'/expensereport/class/expensereport_ik.class.php';
33 require_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 
126  public $paid;
127 
128  // Paiement
132  public $user_paid_infos;
133 
137  public $user_author_infos;
138 
142  public $user_validator_infos;
143 
144  public $rule_warning_message;
145 
146  // ACTIONS
147 
148  // Create
152  public $date_create;
153 
157  public $fk_user_creat;
158 
162  public $fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
163 
164  // Update
168  public $date_modif;
169 
173  public $fk_user_modif;
174 
175  // Refus
179  public $date_refuse;
180 
184  public $detail_refuse;
185 
189  public $fk_user_refuse;
190 
191  // Annulation
195  public $date_cancel;
196 
200  public $detail_cancel;
201 
205  public $fk_user_cancel;
206 
210  public $fk_user_validator;
211 
218  public $datevalid;
219 
224  public $date_valid;
225 
229  public $fk_user_valid;
230 
234  public $user_valid_infos;
235 
236  // Approve
240  public $date_approve;
241 
245  public $fk_user_approve;
246 
247  public $localtax1; // for backward compatibility (real field should be total_localtax1 defined into CommonObject)
248  public $localtax2; // for backward compatibility (real field should be total_localtax2 defined into CommonObject)
249 
253  const STATUS_DRAFT = 0;
254 
258  const STATUS_VALIDATED = 2;
259 
263  const STATUS_CANCELED = 4;
264 
268  const STATUS_APPROVED = 5;
269 
273  const STATUS_CLOSED = 6;
274 
278  const STATUS_REFUSED = 99;
279 
280  public $fields = array(
281  'rowid' => array('type' => 'integer', 'label' => 'ID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10),
282  'ref' => array('type' => 'varchar(50)', 'label' => 'Ref', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'showoncombobox' => 1, 'position' => 15),
283  'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 20),
284  'ref_number_int' => array('type' => 'integer', 'label' => 'Ref number int', 'enabled' => 1, 'visible' => -1, 'position' => 25),
285  'ref_ext' => array('type' => 'integer', 'label' => 'Ref ext', 'enabled' => 1, 'visible' => -1, 'position' => 30),
286  'total_ht' => array('type' => 'double(24,8)', 'label' => 'Total ht', 'enabled' => 1, 'visible' => -1, 'position' => 35),
287  'total_tva' => array('type' => 'double(24,8)', 'label' => 'Total tva', 'enabled' => 1, 'visible' => -1, 'position' => 40),
288  'localtax1' => array('type' => 'double(24,8)', 'label' => 'Localtax1', 'enabled' => 1, 'visible' => -1, 'position' => 45),
289  'localtax2' => array('type' => 'double(24,8)', 'label' => 'Localtax2', 'enabled' => 1, 'visible' => -1, 'position' => 50),
290  'total_ttc' => array('type' => 'double(24,8)', 'label' => 'Total ttc', 'enabled' => 1, 'visible' => -1, 'position' => 55),
291  'date_debut' => array('type' => 'date', 'label' => 'Date debut', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 60),
292  'date_fin' => array('type' => 'date', 'label' => 'Date fin', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 65),
293  'date_valid' => array('type' => 'datetime', 'label' => 'Date valid', 'enabled' => 1, 'visible' => -1, 'position' => 75),
294  'date_approve' => array('type' => 'datetime', 'label' => 'Date approve', 'enabled' => 1, 'visible' => -1, 'position' => 80),
295  'date_refuse' => array('type' => 'datetime', 'label' => 'Date refuse', 'enabled' => 1, 'visible' => -1, 'position' => 85),
296  'date_cancel' => array('type' => 'datetime', 'label' => 'Date cancel', 'enabled' => 1, 'visible' => -1, 'position' => 90),
297  'fk_user_author' => array('type' => 'integer', 'label' => 'Fk user author', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 100),
298  'fk_user_modif' => array('type' => 'integer', 'label' => 'Fk user modif', 'enabled' => 1, 'visible' => -1, 'position' => 105),
299  'fk_user_valid' => array('type' => 'integer', 'label' => 'Fk user valid', 'enabled' => 1, 'visible' => -1, 'position' => 110),
300  'fk_user_validator' => array('type' => 'integer', 'label' => 'Fk user validator', 'enabled' => 1, 'visible' => -1, 'position' => 115),
301  'fk_user_approve' => array('type' => 'integer', 'label' => 'Fk user approve', 'enabled' => 1, 'visible' => -1, 'position' => 120),
302  'fk_user_refuse' => array('type' => 'integer', 'label' => 'Fk user refuse', 'enabled' => 1, 'visible' => -1, 'position' => 125),
303  'fk_user_cancel' => array('type' => 'integer', 'label' => 'Fk user cancel', 'enabled' => 1, 'visible' => -1, 'position' => 130),
304  'fk_c_paiement' => array('type' => 'integer', 'label' => 'Fk c paiement', 'enabled' => 1, 'visible' => -1, 'position' => 140),
305  'paid' => array('type' => 'integer', 'label' => 'Paid', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 145),
306  'note_public' => array('type' => 'html', 'label' => 'Note public', 'enabled' => 1, 'visible' => 0, 'position' => 150),
307  'note_private' => array('type' => 'html', 'label' => 'Note private', 'enabled' => 1, 'visible' => 0, 'position' => 155),
308  'detail_refuse' => array('type' => 'varchar(255)', 'label' => 'Detail refuse', 'enabled' => 1, 'visible' => -1, 'position' => 160),
309  'detail_cancel' => array('type' => 'varchar(255)', 'label' => 'Detail cancel', 'enabled' => 1, 'visible' => -1, 'position' => 165),
310  'integration_compta' => array('type' => 'integer', 'label' => 'Integration compta', 'enabled' => 1, 'visible' => -1, 'position' => 170),
311  'fk_bank_account' => array('type' => 'integer', 'label' => 'Fk bank account', 'enabled' => 1, 'visible' => -1, 'position' => 175),
312  'fk_multicurrency' => array('type' => 'integer', 'label' => 'Fk multicurrency', 'enabled' => 1, 'visible' => -1, 'position' => 185),
313  'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'Multicurrency code', 'enabled' => 1, 'visible' => -1, 'position' => 190),
314  'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'Multicurrency tx', 'enabled' => 1, 'visible' => -1, 'position' => 195),
315  'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'Multicurrency total ht', 'enabled' => 1, 'visible' => -1, 'position' => 200),
316  'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'Multicurrency total tva', 'enabled' => 1, 'visible' => -1, 'position' => 205),
317  'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'Multicurrency total ttc', 'enabled' => 1, 'visible' => -1, 'position' => 210),
318  'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => -1, 'position' => 220),
319  'date_create' => array('type' => 'datetime', 'label' => 'Date create', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 300),
320  'tms' => array('type' => 'timestamp', 'label' => 'Tms', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 305),
321  'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -1, 'position' => 1000),
322  'model_pdf' => array('type' => 'varchar(255)', 'label' => 'Model pdf', 'enabled' => 1, 'visible' => 0, 'position' => 1010),
323  'fk_statut' => array('type' => 'integer', 'label' => 'Fk statut', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 500),
324  );
325 
331  public function __construct($db)
332  {
333  $this->db = $db;
334  $this->total_ht = 0;
335  $this->total_ttc = 0;
336  $this->total_tva = 0;
337  $this->total_localtax1 = 0;
338  $this->total_localtax2 = 0;
339  $this->localtax1 = 0; // For backward compatibility
340  $this->localtax2 = 0; // For backward compatibility
341  $this->modepaymentid = 0;
342 
343  // List of language codes for status
344  $this->labelStatusShort = array(0 => 'Draft', 2 => 'Validated', 4 => 'Canceled', 5 => 'Approved', 6 => 'Paid', 99 => 'Refused');
345  $this->labelStatus = array(0 => 'Draft', 2 => 'ValidatedWaitingApproval', 4 => 'Canceled', 5 => 'Approved', 6 => 'Paid', 99 => 'Refused');
346  }
347 
355  public function create($user, $notrigger = 0)
356  {
357  global $conf, $langs;
358 
359  $now = dol_now();
360 
361  $error = 0;
362 
363  // Check parameters
364  if (empty($this->date_debut) || empty($this->date_fin)) {
365  $this->error = $langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Date'));
366  return -1;
367  }
368 
369  $fuserid = $this->fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
370  if (empty($fuserid)) {
371  $fuserid = $user->id;
372  }
373 
374  $this->db->begin();
375 
376  $sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
377  $sql .= "ref";
378  $sql .= ",total_ht";
379  $sql .= ",total_ttc";
380  $sql .= ",total_tva";
381  $sql .= ",date_debut";
382  $sql .= ",date_fin";
383  $sql .= ",date_create";
384  $sql .= ",fk_user_creat";
385  $sql .= ",fk_user_author";
386  $sql .= ",fk_user_validator";
387  $sql .= ",fk_user_approve";
388  $sql .= ",fk_user_modif";
389  $sql .= ",fk_statut";
390  $sql .= ",fk_c_paiement";
391  $sql .= ",paid";
392  $sql .= ",note_public";
393  $sql .= ",note_private";
394  $sql .= ",entity";
395  $sql .= ") VALUES(";
396  $sql .= "'(PROV)'";
397  $sql .= ", ".price2num($this->total_ht, 'MT');
398  $sql .= ", ".price2num($this->total_ttc, 'MT');
399  $sql .= ", ".price2num($this->total_tva, 'MT');
400  $sql .= ", '".$this->db->idate($this->date_debut)."'";
401  $sql .= ", '".$this->db->idate($this->date_fin)."'";
402  $sql .= ", '".$this->db->idate($now)."'";
403  $sql .= ", ".((int) $user->id);
404  $sql .= ", ".((int) $fuserid);
405  $sql .= ", ".($this->fk_user_validator > 0 ? ((int) $this->fk_user_validator) : "null");
406  $sql .= ", ".($this->fk_user_approve > 0 ? ((int) $this->fk_user_approve) : "null");
407  $sql .= ", ".($this->fk_user_modif > 0 ? ((int) $this->fk_user_modif) : "null");
408  $sql .= ", ".($this->fk_statut > 1 ? ((int) $this->fk_statut) : 0);
409  $sql .= ", ".($this->modepaymentid ? ((int) $this->modepaymentid) : "null");
410  $sql .= ", 0";
411  $sql .= ", ".($this->note_public ? "'".$this->db->escape($this->note_public)."'" : "null");
412  $sql .= ", ".($this->note_private ? "'".$this->db->escape($this->note_private)."'" : "null");
413  $sql .= ", ".((int) $conf->entity);
414  $sql .= ")";
415 
416  $result = $this->db->query($sql);
417  if ($result) {
418  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
419  $this->ref = '(PROV'.$this->id.')';
420 
421  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element." SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
422  $resql = $this->db->query($sql);
423  if (!$resql) {
424  $this->error = $this->db->lasterror();
425  $error++;
426  }
427 
428  if (!$error) {
429  if (is_array($this->lines) && count($this->lines) > 0) {
430  foreach ($this->lines as $line) {
431  // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
432  //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
433  if (!is_object($line)) {
434  $line = (object) $line;
435  $newndfline = new ExpenseReportLine($this->db);
436  $newndfline->fk_expensereport = $line->fk_expensereport;
437  $newndfline->fk_c_type_fees = $line->fk_c_type_fees;
438  $newndfline->fk_project = $line->fk_project;
439  $newndfline->vatrate = $line->vatrate;
440  $newndfline->vat_src_code = $line->vat_src_code;
441  $newndfline->localtax1_tx = $line->localtax1_tx;
442  $newndfline->localtax2_tx = $line->localtax2_tx;
443  $newndfline->localtax1_type = $line->localtax1_type;
444  $newndfline->localtax2_type = $line->localtax2_type;
445  $newndfline->comments = $line->comments;
446  $newndfline->qty = $line->qty;
447  $newndfline->value_unit = $line->value_unit;
448  $newndfline->total_ht = $line->total_ht;
449  $newndfline->total_ttc = $line->total_ttc;
450  $newndfline->total_tva = $line->total_tva;
451  $newndfline->total_localtax1 = $line->total_localtax1;
452  $newndfline->total_localtax2 = $line->total_localtax2;
453  $newndfline->date = $line->date;
454  $newndfline->rule_warning_message = $line->rule_warning_message;
455  $newndfline->fk_c_exp_tax_cat = $line->fk_c_exp_tax_cat;
456  $newndfline->fk_ecm_files = $line->fk_ecm_files;
457  } else {
458  $newndfline = $line;
459  }
460  //$newndfline=new ExpenseReportLine($this->db);
461  $newndfline->fk_expensereport = $this->id;
462  $result = $newndfline->insert();
463  if ($result < 0) {
464  $this->error = $newndfline->error;
465  $this->errors = $newndfline->errors;
466  $error++;
467  break;
468  }
469  }
470  }
471  }
472 
473  if (!$error) {
474  $result = $this->insertExtraFields();
475  if ($result < 0) {
476  $error++;
477  }
478  }
479 
480  if (!$error) {
481  $result = $this->update_price(1);
482  if ($result > 0) {
483  if (!$notrigger) {
484  // Call trigger
485  $result = $this->call_trigger('EXPENSE_REPORT_CREATE', $user);
486 
487  if ($result < 0) {
488  $error++;
489  }
490  // End call triggers
491  }
492 
493  if (empty($error)) {
494  $this->db->commit();
495  return $this->id;
496  } else {
497  $this->db->rollback();
498  return -4;
499  }
500  } else {
501  $this->db->rollback();
502  return -3;
503  }
504  } else {
505  dol_syslog(get_class($this)."::create error ".$this->error, LOG_ERR);
506  $this->db->rollback();
507  return -2;
508  }
509  } else {
510  $this->error = $this->db->lasterror()." sql=".$sql;
511  $this->db->rollback();
512  return -1;
513  }
514  }
515 
523  public function createFromClone(User $user, $fk_user_author)
524  {
525  global $hookmanager;
526 
527  $error = 0;
528 
529  if (empty($fk_user_author)) {
530  $fk_user_author = $user->id;
531  }
532 
533  $this->db->begin();
534 
535  // get extrafields so they will be clone
536  //foreach($this->lines as $line)
537  //$line->fetch_optionals();
538 
539  // Load source object
540  $objFrom = clone $this;
541 
542  $this->id = 0;
543  $this->ref = '';
544  $this->status = 0;
545  $this->fk_statut = 0; // deprecated
546 
547  // Clear fields
548  $this->fk_user_creat = $user->id;
549  $this->fk_user_author = $fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
550  $this->fk_user_valid = 0;
551  $this->date_create = '';
552  $this->date_creation = '';
553  $this->date_validation = '';
554 
555  // Remove link on lines to a joined file
556  if (is_array($this->lines) && count($this->lines) > 0) {
557  foreach ($this->lines as $key => $line) {
558  $this->lines[$key]->fk_ecm_files = 0;
559  }
560  }
561 
562  // Create clone
563  $this->context['createfromclone'] = 'createfromclone';
564  $result = $this->create($user);
565  if ($result < 0) {
566  $error++;
567  }
568 
569  if (!$error) {
570  // Hook of thirdparty module
571  if (is_object($hookmanager)) {
572  $parameters = array('objFrom' => $objFrom);
573  $action = '';
574  $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
575  if ($reshook < 0) {
576  $this->setErrorsFromObject($hookmanager);
577  $error++;
578  }
579  }
580  }
581 
582  unset($this->context['createfromclone']);
583 
584  // End
585  if (!$error) {
586  $this->db->commit();
587  return $this->id;
588  } else {
589  $this->db->rollback();
590  return -1;
591  }
592  }
593 
594 
603  public function update($user, $notrigger = 0, $userofexpensereport = null)
604  {
605  global $langs;
606 
607  $error = 0;
608  $this->db->begin();
609 
610  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
611  $sql .= " total_ht = ".$this->total_ht;
612  $sql .= " , total_ttc = ".$this->total_ttc;
613  $sql .= " , total_tva = ".$this->total_tva;
614  $sql .= " , date_debut = '".$this->db->idate($this->date_debut)."'";
615  $sql .= " , date_fin = '".$this->db->idate($this->date_fin)."'";
616  if ($userofexpensereport && is_object($userofexpensereport)) {
617  $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.
618  }
619  $sql .= " , fk_user_validator = ".($this->fk_user_validator > 0 ? $this->fk_user_validator : "null");
620  $sql .= " , fk_user_valid = ".($this->fk_user_valid > 0 ? $this->fk_user_valid : "null");
621  $sql .= " , fk_user_approve = ".($this->fk_user_approve > 0 ? $this->fk_user_approve : "null");
622  $sql .= " , fk_user_modif = ".$user->id;
623  $sql .= " , fk_statut = ".($this->fk_statut >= 0 ? $this->fk_statut : '0');
624  $sql .= " , fk_c_paiement = ".($this->fk_c_paiement > 0 ? $this->fk_c_paiement : "null");
625  $sql .= " , note_public = ".(!empty($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "''");
626  $sql .= " , note_private = ".(!empty($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "''");
627  $sql .= " , detail_refuse = ".(!empty($this->detail_refuse) ? "'".$this->db->escape($this->detail_refuse)."'" : "''");
628  $sql .= " WHERE rowid = ".((int) $this->id);
629 
630  dol_syslog(get_class($this)."::update", LOG_DEBUG);
631  $result = $this->db->query($sql);
632  if ($result) {
633  if (!$notrigger) {
634  // Call trigger
635  $result = $this->call_trigger('EXPENSE_REPORT_MODIFY', $user);
636 
637  if ($result < 0) {
638  $error++;
639  }
640  // End call triggers
641  }
642 
643  if (empty($error)) {
644  $this->db->commit();
645  return 1;
646  } else {
647  $this->db->rollback();
648  $this->error = $this->db->error();
649  return -2;
650  }
651  } else {
652  $this->db->rollback();
653  $this->error = $this->db->error();
654  return -1;
655  }
656  }
657 
665  public function fetch($id, $ref = '')
666  {
667  $sql = "SELECT d.rowid, d.entity, d.ref, d.note_public, d.note_private,"; // DEFAULT
668  $sql .= " d.detail_refuse, d.detail_cancel, d.fk_user_refuse, d.fk_user_cancel,"; // ACTIONS
669  $sql .= " d.date_refuse, d.date_cancel,"; // ACTIONS
670  $sql .= " d.total_ht, d.total_ttc, d.total_tva,";
671  $sql .= " d.localtax1 as total_localtax1, d.localtax2 as total_localtax2,";
672  $sql .= " d.date_debut, d.date_fin, d.date_create, d.tms as date_modif, d.date_valid, d.date_approve,"; // DATES (datetime)
673  $sql .= " d.fk_user_creat, d.fk_user_author, d.fk_user_modif, d.fk_user_validator,";
674  $sql .= " d.fk_user_valid, d.fk_user_approve,";
675  $sql .= " d.fk_statut as status, d.fk_c_paiement, d.paid";
676  $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as d";
677  if ($ref) {
678  $sql .= " WHERE d.ref = '".$this->db->escape($ref)."'";
679  } else {
680  $sql .= " WHERE d.rowid = ".((int) $id);
681  }
682  //$sql.= $restrict;
683 
684  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
685  $resql = $this->db->query($sql);
686  if ($resql) {
687  $obj = $this->db->fetch_object($resql);
688  if ($obj) {
689  $this->id = $obj->rowid;
690  $this->ref = $obj->ref;
691 
692  $this->entity = $obj->entity;
693 
694  $this->total_ht = $obj->total_ht;
695  $this->total_tva = $obj->total_tva;
696  $this->total_ttc = $obj->total_ttc;
697  $this->localtax1 = $obj->total_localtax1; // For backward compatibility
698  $this->localtax2 = $obj->total_localtax2; // For backward compatibility
699  $this->total_localtax1 = $obj->total_localtax1;
700  $this->total_localtax2 = $obj->total_localtax2;
701 
702  $this->note_public = $obj->note_public;
703  $this->note_private = $obj->note_private;
704  $this->detail_refuse = $obj->detail_refuse;
705  $this->detail_cancel = $obj->detail_cancel;
706 
707  $this->date_debut = $this->db->jdate($obj->date_debut);
708  $this->date_fin = $this->db->jdate($obj->date_fin);
709  $this->date_valid = $this->db->jdate($obj->date_valid);
710  $this->date_approve = $this->db->jdate($obj->date_approve);
711  $this->date_create = $this->db->jdate($obj->date_create);
712  $this->date_modif = $this->db->jdate($obj->date_modif);
713  $this->date_refuse = $this->db->jdate($obj->date_refuse);
714  $this->date_cancel = $this->db->jdate($obj->date_cancel);
715 
716  $this->fk_user_creat = $obj->fk_user_creat;
717  $this->fk_user_author = $obj->fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
718  $this->fk_user_modif = $obj->fk_user_modif;
719  $this->fk_user_validator = $obj->fk_user_validator;
720  $this->fk_user_valid = $obj->fk_user_valid;
721  $this->fk_user_refuse = $obj->fk_user_refuse;
722  $this->fk_user_cancel = $obj->fk_user_cancel;
723  $this->fk_user_approve = $obj->fk_user_approve;
724 
725  $user_author = new User($this->db);
726  if ($this->fk_user_author > 0) {
727  $user_author->fetch($this->fk_user_author);
728  }
729 
730  $this->user_author_infos = dolGetFirstLastname($user_author->firstname, $user_author->lastname);
731 
732  $user_approver = new User($this->db);
733  if ($this->fk_user_approve > 0) {
734  $user_approver->fetch($this->fk_user_approve);
735  } elseif ($this->fk_user_validator > 0) {
736  $user_approver->fetch($this->fk_user_validator); // For backward compatibility
737  }
738  $this->user_validator_infos = dolGetFirstLastname($user_approver->firstname, $user_approver->lastname);
739 
740  $this->fk_statut = $obj->status; // deprecated
741  $this->status = $obj->status;
742  $this->fk_c_paiement = $obj->fk_c_paiement;
743  $this->paid = $obj->paid;
744 
745  if ($this->status == self::STATUS_APPROVED || $this->status == self::STATUS_CLOSED) {
746  $user_valid = new User($this->db);
747  if ($this->fk_user_valid > 0) {
748  $user_valid->fetch($this->fk_user_valid);
749  }
750  $this->user_valid_infos = dolGetFirstLastname($user_valid->firstname, $user_valid->lastname);
751  }
752 
753  $this->fetch_optionals();
754 
755  $result = $this->fetch_lines();
756 
757  return $result;
758  } else {
759  return 0;
760  }
761  } else {
762  $this->error = $this->db->lasterror();
763  return -1;
764  }
765  }
766 
767  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
778  public function set_paid($id, $fuser, $notrigger = 0)
779  {
780  // phpcs:enable
781  dol_syslog(get_class($this)."::set_paid is deprecated, use setPaid instead", LOG_NOTICE);
782  return $this->setPaid($id, $fuser, $notrigger);
783  }
784 
793  public function setPaid($id, $fuser, $notrigger = 0)
794  {
795  $error = 0;
796  $this->db->begin();
797 
798  $sql = "UPDATE ".MAIN_DB_PREFIX."expensereport";
799  $sql .= " SET fk_statut = ".self::STATUS_CLOSED.", paid=1";
800  $sql .= " WHERE rowid = ".((int) $id)." AND fk_statut = ".self::STATUS_APPROVED;
801 
802  dol_syslog(get_class($this)."::setPaid", LOG_DEBUG);
803  $resql = $this->db->query($sql);
804  if ($resql) {
805  if ($this->db->affected_rows($resql)) {
806  if (!$notrigger) {
807  // Call trigger
808  $result = $this->call_trigger('EXPENSE_REPORT_PAID', $fuser);
809 
810  if ($result < 0) {
811  $error++;
812  }
813  // End call triggers
814  }
815 
816  if (empty($error)) {
817  $this->db->commit();
818  return 1;
819  } else {
820  $this->db->rollback();
821  $this->error = $this->db->error();
822  return -2;
823  }
824  } else {
825  $this->db->commit();
826  return 0;
827  }
828  } else {
829  $this->db->rollback();
830  dol_print_error($this->db);
831  return -1;
832  }
833  }
834 
841  public function getLibStatut($mode = 0)
842  {
843  return $this->LibStatut($this->status, $mode);
844  }
845 
846  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
854  public function LibStatut($status, $mode = 0)
855  {
856  // phpcs:enable
857  global $langs;
858 
859  $labelStatus = $langs->transnoentitiesnoconv($this->labelStatus[$status]);
860  $labelStatusShort = $langs->transnoentitiesnoconv($this->labelStatusShort[$status]);
861 
862  $statuslogo = array(0 => 'status0', 2 => 'status1', 4 => 'status6', 5 => 'status4', 6 => 'status6', 99 => 'status5');
863 
864  $statusType = $statuslogo[$status];
865 
866  return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode);
867  }
868 
869 
876  public function info($id)
877  {
878  global $conf;
879 
880  $sql = "SELECT f.rowid,";
881  $sql .= " f.date_create as datec,";
882  $sql .= " f.tms as date_modification,";
883  $sql .= " f.date_valid as datev,";
884  $sql .= " f.date_approve as datea,";
885  $sql .= " f.fk_user_creat as fk_user_creation,";
886  $sql .= " f.fk_user_author as fk_user_author,";
887  $sql .= " f.fk_user_modif as fk_user_modification,";
888  $sql .= " f.fk_user_valid,";
889  $sql .= " f.fk_user_approve";
890  $sql .= " FROM ".MAIN_DB_PREFIX."expensereport as f";
891  $sql .= " WHERE f.rowid = ".((int) $id);
892  $sql .= " AND f.entity = ".$conf->entity;
893 
894 
895 
896  $resql = $this->db->query($sql);
897  if ($resql) {
898  if ($this->db->num_rows($resql)) {
899  $obj = $this->db->fetch_object($resql);
900 
901  $this->id = $obj->rowid;
902 
903  $this->date_creation = $this->db->jdate($obj->datec);
904  $this->date_modification = $this->db->jdate($obj->date_modification);
905  $this->date_validation = $this->db->jdate($obj->datev);
906  $this->date_approbation = $this->db->jdate($obj->datea);
907 
908  $this->user_creation_id = $obj->fk_user_author;
909  $this->user_creation_id = $obj->fk_user_creation;
910  $this->user_validation_id = $obj->fk_user_valid;
911  $this->user_modification_id = $obj->fk_user_modification;
912  $this->user_approve_id = $obj->fk_user_approve;
913  }
914  $this->db->free($resql);
915  } else {
916  dol_print_error($this->db);
917  }
918  }
919 
920 
921 
929  public function initAsSpecimen()
930  {
931  global $user, $langs;
932 
933  $now = dol_now();
934 
935  // Initialise parameters
936  $this->id = 0;
937  $this->ref = 'SPECIMEN';
938  $this->specimen = 1;
939  $this->entity = 1;
940  $this->date_create = $now;
941  $this->date_debut = $now;
942  $this->date_fin = $now;
943  $this->date_valid = $now;
944  $this->date_approve = $now;
945 
946  $type_fees_id = 2; // TF_TRIP
947 
948  $this->status = 5;
949 
950  $this->fk_user_author = $user->id;
951  $this->fk_user_validator = $user->id;
952  $this->fk_user_valid = $user->id;
953  $this->fk_user_approve = $user->id;
954 
955  $this->note_private = 'Private note';
956  $this->note_public = 'SPECIMEN';
957  $nbp = 5;
958  $xnbp = 0;
959  while ($xnbp < $nbp) {
960  $line = new ExpenseReportLine($this->db);
961  $line->comments = $langs->trans("Comment")." ".$xnbp;
962  $line->date = ($now - 3600 * (1 + $xnbp));
963  $line->total_ht = 100;
964  $line->total_tva = 20;
965  $line->total_ttc = 120;
966  $line->qty = 1;
967  $line->vatrate = 20;
968  $line->value_unit = 120;
969  $line->fk_expensereport = 0;
970  $line->type_fees_code = 'TRA';
971  $line->fk_c_type_fees = $type_fees_id;
972 
973  $line->projet_ref = 'ABC';
974 
975  $this->lines[$xnbp] = $line;
976  $xnbp++;
977 
978  $this->total_ht += $line->total_ht;
979  $this->total_tva += $line->total_tva;
980  $this->total_ttc += $line->total_ttc;
981  }
982 
983  return 1;
984  }
985 
986  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
994  public function fetch_line_by_project($projectid, $user)
995  {
996  // phpcs:enable
997  global $langs;
998 
999  $langs->load('trips');
1000 
1001  if ($user->hasRight('expensereport', 'lire')) {
1002  $sql = "SELECT de.fk_expensereport, de.date, de.comments, de.total_ht, de.total_ttc";
1003  $sql .= " FROM ".MAIN_DB_PREFIX."expensereport_det as de";
1004  $sql .= " WHERE de.fk_projet = ".((int) $projectid);
1005 
1006  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
1007  $result = $this->db->query($sql);
1008  if ($result) {
1009  $num = $this->db->num_rows($result);
1010  $i = 0;
1011  $total_HT = 0;
1012  $total_TTC = 0;
1013 
1014  while ($i < $num) {
1015  $objp = $this->db->fetch_object($result);
1016 
1017  $sql2 = "SELECT d.rowid, d.fk_user_author, d.ref, d.fk_statut as status";
1018  $sql2 .= " FROM ".MAIN_DB_PREFIX."expensereport as d";
1019  $sql2 .= " WHERE d.rowid = ".((int) $objp->fk_expensereport);
1020 
1021  $result2 = $this->db->query($sql2);
1022  $obj = $this->db->fetch_object($result2);
1023 
1024  $objp->fk_user_author = $obj->fk_user_author;
1025  $objp->ref = $obj->ref;
1026  $objp->fk_c_expensereport_status = $obj->status;
1027  $objp->rowid = $obj->rowid;
1028 
1029  $total_HT += $objp->total_ht;
1030  $total_TTC += $objp->total_ttc;
1031  $author = new User($this->db);
1032  $author->fetch($objp->fk_user_author);
1033 
1034  print '<tr>';
1035  print '<td><a href="'.DOL_URL_ROOT.'/expensereport/card.php?id='.$objp->rowid.'">'.$objp->ref_num.'</a></td>';
1036  print '<td class="center">'.dol_print_date($objp->date, 'day').'</td>';
1037  print '<td>'.$author->getNomUrl(1).'</td>';
1038  print '<td>'.$objp->comments.'</td>';
1039  print '<td class="right">'.price($objp->total_ht).'</td>';
1040  print '<td class="right">'.price($objp->total_ttc).'</td>';
1041  print '<td class="right">';
1042 
1043  switch ($objp->fk_c_expensereport_status) {
1044  case 4:
1045  print img_picto($langs->trans('StatusOrderCanceled'), 'statut5');
1046  break;
1047  case 1:
1048  print $langs->trans('Draft').' '.img_picto($langs->trans('Draft'), 'statut0');
1049  break;
1050  case 2:
1051  print $langs->trans('TripForValid').' '.img_picto($langs->trans('TripForValid'), 'statut3');
1052  break;
1053  case 5:
1054  print $langs->trans('TripForPaid').' '.img_picto($langs->trans('TripForPaid'), 'statut3');
1055  break;
1056  case 6:
1057  print $langs->trans('TripPaid').' '.img_picto($langs->trans('TripPaid'), 'statut4');
1058  break;
1059  }
1060  /*
1061  if ($status==4) return img_picto($langs->trans('StatusOrderCanceled'),'statut5');
1062  if ($status==1) return img_picto($langs->trans('StatusOrderDraft'),'statut0');
1063  if ($status==2) return img_picto($langs->trans('StatusOrderValidated'),'statut1');
1064  if ($status==2) return img_picto($langs->trans('StatusOrderOnProcess'),'statut3');
1065  if ($status==5) return img_picto($langs->trans('StatusOrderToBill'),'statut4');
1066  if ($status==6) return img_picto($langs->trans('StatusOrderOnProcess'),'statut6');
1067  */
1068  print '</td>';
1069  print '</tr>';
1070 
1071  $i++;
1072  }
1073 
1074  print '<tr class="liste_total"><td colspan="4">'.$langs->trans("Number").': '.$i.'</td>';
1075  print '<td class="right" width="100">'.$langs->trans("TotalHT").' : '.price($total_HT).'</td>';
1076  print '<td class="right" width="100">'.$langs->trans("TotalTTC").' : '.price($total_TTC).'</td>';
1077  print '<td>&nbsp;</td>';
1078  print '</tr>';
1079  } else {
1080  $this->error = $this->db->lasterror();
1081  return -1;
1082  }
1083  }
1084 
1085  return 0;
1086  }
1087 
1088  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1094  public function fetch_lines()
1095  {
1096  // phpcs:enable
1097  $this->lines = array();
1098 
1099  $sql = ' SELECT de.rowid, de.comments, de.qty, de.value_unit, de.date, de.rang,';
1100  $sql .= " de.".$this->fk_element.", de.fk_c_type_fees, de.fk_c_exp_tax_cat, de.fk_projet as fk_project,";
1101  $sql .= ' de.tva_tx, de.vat_src_code,';
1102  $sql .= ' de.localtax1_tx, de.localtax2_tx, de.localtax1_type, de.localtax2_type,';
1103  $sql .= ' de.fk_ecm_files,';
1104  $sql .= ' de.total_ht, de.total_tva, de.total_ttc,';
1105  $sql .= ' de.total_localtax1, de.total_localtax2, de.rule_warning_message,';
1106  $sql .= ' ctf.code as code_type_fees, ctf.label as label_type_fees, ctf.accountancy_code as accountancy_code_type_fees,';
1107  $sql .= ' p.ref as ref_projet, p.title as title_projet';
1108  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line.' as de';
1109  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_type_fees as ctf ON de.fk_c_type_fees = ctf.id';
1110  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'projet as p ON de.fk_projet = p.rowid';
1111  $sql .= " WHERE de.".$this->fk_element." = ".((int) $this->id);
1112  if (getDolGlobalString('EXPENSEREPORT_LINES_SORTED_BY_ROWID')) {
1113  $sql .= ' ORDER BY de.rang ASC, de.rowid ASC';
1114  } else {
1115  $sql .= ' ORDER BY de.rang ASC, de.date ASC';
1116  }
1117 
1118  $resql = $this->db->query($sql);
1119  if ($resql) {
1120  $num = $this->db->num_rows($resql);
1121  $i = 0;
1122  while ($i < $num) {
1123  $objp = $this->db->fetch_object($resql);
1124 
1125  $deplig = new ExpenseReportLine($this->db);
1126 
1127  $deplig->rowid = $objp->rowid;
1128  $deplig->id = $objp->rowid;
1129  $deplig->comments = $objp->comments;
1130  $deplig->qty = $objp->qty;
1131  $deplig->value_unit = $objp->value_unit;
1132  $deplig->date = $objp->date;
1133  $deplig->dates = $this->db->jdate($objp->date);
1134 
1135  $deplig->fk_expensereport = $objp->fk_expensereport;
1136  $deplig->fk_c_type_fees = $objp->fk_c_type_fees;
1137  $deplig->fk_c_exp_tax_cat = $objp->fk_c_exp_tax_cat;
1138  $deplig->fk_projet = $objp->fk_project; // deprecated
1139  $deplig->fk_project = $objp->fk_project;
1140  $deplig->fk_ecm_files = $objp->fk_ecm_files;
1141 
1142  $deplig->total_ht = $objp->total_ht;
1143  $deplig->total_tva = $objp->total_tva;
1144  $deplig->total_ttc = $objp->total_ttc;
1145  $deplig->total_localtax1 = $objp->total_localtax1;
1146  $deplig->total_localtax2 = $objp->total_localtax2;
1147 
1148  $deplig->type_fees_code = empty($objp->code_type_fees) ? 'TF_OTHER' : $objp->code_type_fees;
1149  $deplig->type_fees_libelle = $objp->label_type_fees;
1150  $deplig->type_fees_accountancy_code = $objp->accountancy_code_type_fees;
1151 
1152  $deplig->tva_tx = $objp->tva_tx;
1153  $deplig->vatrate = $objp->tva_tx;
1154  $deplig->vat_src_code = $objp->vat_src_code;
1155  $deplig->localtax1_tx = $objp->localtax1_tx;
1156  $deplig->localtax2_tx = $objp->localtax2_tx;
1157  $deplig->localtax1_type = $objp->localtax1_type;
1158  $deplig->localtax2_type = $objp->localtax2_type;
1159 
1160  $deplig->projet_ref = $objp->ref_projet;
1161  $deplig->projet_title = $objp->title_projet;
1162 
1163  $deplig->rule_warning_message = $objp->rule_warning_message;
1164 
1165  $deplig->rang = $objp->rang;
1166 
1167  $this->lines[$i] = $deplig;
1168 
1169  $i++;
1170  }
1171  $this->db->free($resql);
1172  return 1;
1173  } else {
1174  $this->error = $this->db->lasterror();
1175  dol_syslog(get_class($this)."::fetch_lines: Error ".$this->error, LOG_ERR);
1176  return -3;
1177  }
1178  }
1179 
1180 
1188  public function delete(User $user = null, $notrigger = 0)
1189  {
1190  global $conf;
1191  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1192 
1193  $error = 0;
1194 
1195  $this->db->begin();
1196 
1197  if (!$notrigger) {
1198  // Call trigger
1199  $result = $this->call_trigger('EXPENSE_REPORT_DELETE', $user);
1200  if ($result < 0) {
1201  $error++;
1202  }
1203  // End call triggers
1204  }
1205 
1206  // Delete extrafields of lines and lines
1207  if (!$error && !empty($this->table_element_line)) {
1208  $tabletodelete = $this->table_element_line;
1209  //$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).")";
1210  $sql = "DELETE FROM ".MAIN_DB_PREFIX.$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id);
1211  if (!$this->db->query($sql)) {
1212  $error++;
1213  $this->error = $this->db->lasterror();
1214  $this->errors[] = $this->error;
1215  dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
1216  }
1217  }
1218 
1219  if (!$error) {
1220  // Delete linked object
1221  $res = $this->deleteObjectLinked();
1222  if ($res < 0) {
1223  $error++;
1224  }
1225  }
1226 
1227  if (!$error) {
1228  // Delete linked contacts
1229  $res = $this->delete_linked_contact();
1230  if ($res < 0) {
1231  $error++;
1232  }
1233  }
1234 
1235  // Removed extrafields of object
1236  if (!$error) {
1237  $result = $this->deleteExtraFields();
1238  if ($result < 0) {
1239  $error++;
1240  dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
1241  }
1242  }
1243 
1244  // Delete main record
1245  if (!$error) {
1246  $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element." WHERE rowid = ".((int) $this->id);
1247  $res = $this->db->query($sql);
1248  if (!$res) {
1249  $error++;
1250  $this->error = $this->db->lasterror();
1251  $this->errors[] = $this->error;
1252  dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
1253  }
1254  }
1255 
1256  // Delete record into ECM index and physically
1257  if (!$error) {
1258  $res = $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
1259  $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
1260  if (!$res) {
1261  $error++;
1262  }
1263  }
1264 
1265  if (!$error) {
1266  // We remove directory
1267  $ref = dol_sanitizeFileName($this->ref);
1268  if ($conf->expensereport->multidir_output[$this->entity] && !empty($this->ref)) {
1269  $dir = $conf->expensereport->multidir_output[$this->entity]."/".$ref;
1270  $file = $dir."/".$ref.".pdf";
1271  if (file_exists($file)) {
1272  dol_delete_preview($this);
1273 
1274  if (!dol_delete_file($file, 0, 0, 0, $this)) {
1275  $this->error = 'ErrorFailToDeleteFile';
1276  $this->errors[] = $this->error;
1277  $this->db->rollback();
1278  return 0;
1279  }
1280  }
1281  if (file_exists($dir)) {
1282  $res = @dol_delete_dir_recursive($dir);
1283  if (!$res) {
1284  $this->error = 'ErrorFailToDeleteDir';
1285  $this->errors[] = $this->error;
1286  $this->db->rollback();
1287  return 0;
1288  }
1289  }
1290  }
1291  }
1292 
1293  if (!$error) {
1294  dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
1295  $this->db->commit();
1296  return 1;
1297  } else {
1298  $this->db->rollback();
1299  return -1;
1300  }
1301  }
1302 
1310  public function setValidate($fuser, $notrigger = 0)
1311  {
1312  global $conf, $langs, $user;
1313 
1314  $error = 0;
1315  $now = dol_now();
1316 
1317  // Protection
1318  if ($this->status == self::STATUS_VALIDATED) {
1319  dol_syslog(get_class($this)."::valid action abandoned: already validated", LOG_WARNING);
1320  return 0;
1321  }
1322 
1323  $this->date_valid = $now; // Required for the getNextNum later.
1324 
1325  // Define new ref
1326  if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
1327  $num = $this->getNextNumRef();
1328  } else {
1329  $num = $this->ref;
1330  }
1331  if (empty($num) || $num < 0) {
1332  return -1;
1333  }
1334 
1335  $this->newref = dol_sanitizeFileName($num);
1336 
1337  $this->db->begin();
1338 
1339  // Validate
1340  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
1341  $sql .= " SET ref = '".$this->db->escape($num)."',";
1342  $sql .= " fk_statut = ".self::STATUS_VALIDATED.",";
1343  $sql .= " date_valid = '".$this->db->idate($this->date_valid)."',";
1344  $sql .= " fk_user_valid = ".((int) $user->id);
1345  $sql .= " WHERE rowid = ".((int) $this->id);
1346 
1347  $resql = $this->db->query($sql);
1348  if ($resql) {
1349  if (!$error && !$notrigger) {
1350  // Call trigger
1351  $result = $this->call_trigger('EXPENSE_REPORT_VALIDATE', $fuser);
1352  if ($result < 0) {
1353  $error++;
1354  }
1355  // End call triggers
1356  }
1357 
1358  if (!$error) {
1359  $this->oldref = $this->ref;
1360 
1361  // Rename directory if dir was a temporary ref
1362  if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1363  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1364 
1365  // Now we rename also files into index
1366  $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)."'";
1367  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'expensereport/".$this->db->escape($this->ref)."' AND entity = ".((int) $this->entity);
1368  $resql = $this->db->query($sql);
1369  if (!$resql) {
1370  $error++;
1371  $this->error = $this->db->lasterror();
1372  }
1373  $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'expensereport/".$this->db->escape($this->newref)."'";
1374  $sql .= " WHERE filepath = 'expensereport/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1375  $resql = $this->db->query($sql);
1376  if (!$resql) {
1377  $error++;
1378  $this->error = $this->db->lasterror();
1379  }
1380 
1381  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1382  $oldref = dol_sanitizeFileName($this->ref);
1383  $newref = dol_sanitizeFileName($num);
1384  $dirsource = $conf->expensereport->multidir_output[$this->entity].'/'.$oldref;
1385  $dirdest = $conf->expensereport->multidir_output[$this->entity].'/'.$newref;
1386  if (!$error && file_exists($dirsource)) {
1387  dol_syslog(get_class($this)."::setValidate() rename dir ".$dirsource." into ".$dirdest);
1388 
1389  if (@rename($dirsource, $dirdest)) {
1390  dol_syslog("Rename ok");
1391  // Rename docs starting with $oldref with $newref
1392  $listoffiles = dol_dir_list($dirdest, 'files', 1, '^'.preg_quote($oldref, '/'));
1393  foreach ($listoffiles as $fileentry) {
1394  $dirsource = $fileentry['name'];
1395  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1396  $dirsource = $fileentry['path'].'/'.$dirsource;
1397  $dirdest = $fileentry['path'].'/'.$dirdest;
1398  @rename($dirsource, $dirdest);
1399  }
1400  }
1401  }
1402  }
1403  }
1404 
1405  // Set new ref and current status
1406  if (!$error) {
1407  $this->ref = $num;
1408  $this->status = self::STATUS_VALIDATED;
1409  }
1410 
1411  if (empty($error)) {
1412  $this->db->commit();
1413  return 1;
1414  } else {
1415  $this->db->rollback();
1416  $this->error = $this->db->error();
1417  return -2;
1418  }
1419  } else {
1420  $this->db->rollback();
1421  $this->error = $this->db->lasterror();
1422  return -1;
1423  }
1424  }
1425 
1426  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1433  public function set_save_from_refuse($fuser)
1434  {
1435  // phpcs:enable
1436  // Sélection de la date de début de la NDF
1437  $sql = 'SELECT date_debut';
1438  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element;
1439  $sql .= " WHERE rowid = ".((int) $this->id);
1440 
1441  $result = $this->db->query($sql);
1442 
1443  $objp = $this->db->fetch_object($result);
1444 
1445  $this->date_debut = $this->db->jdate($objp->date_debut);
1446 
1447  if ($this->status != self::STATUS_VALIDATED) {
1448  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1449  $sql .= " SET fk_statut = ".self::STATUS_VALIDATED;
1450  $sql .= " WHERE rowid = ".((int) $this->id);
1451 
1452  dol_syslog(get_class($this)."::set_save_from_refuse", LOG_DEBUG);
1453 
1454  if ($this->db->query($sql)) {
1455  return 1;
1456  } else {
1457  $this->error = $this->db->lasterror();
1458  return -1;
1459  }
1460  } else {
1461  dol_syslog(get_class($this)."::set_save_from_refuse expensereport already with save status", LOG_WARNING);
1462  }
1463 
1464  return 0;
1465  }
1466 
1474  public function setApproved($fuser, $notrigger = 0)
1475  {
1476  $now = dol_now();
1477  $error = 0;
1478 
1479  // date approval
1480  $this->date_approve = $now;
1481  if ($this->status != self::STATUS_APPROVED) {
1482  $this->db->begin();
1483 
1484  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1485  $sql .= " SET ref = '".$this->db->escape($this->ref)."', fk_statut = ".self::STATUS_APPROVED.", fk_user_approve = ".((int) $fuser->id).",";
1486  $sql .= " date_approve='".$this->db->idate($this->date_approve)."'";
1487  $sql .= " WHERE rowid = ".((int) $this->id);
1488  if ($this->db->query($sql)) {
1489  if (!$notrigger) {
1490  // Call trigger
1491  $result = $this->call_trigger('EXPENSE_REPORT_APPROVE', $fuser);
1492 
1493  if ($result < 0) {
1494  $error++;
1495  }
1496  // End call triggers
1497  }
1498 
1499  if (empty($error)) {
1500  $this->db->commit();
1501  return 1;
1502  } else {
1503  $this->db->rollback();
1504  $this->error = $this->db->error();
1505  return -2;
1506  }
1507  } else {
1508  $this->db->rollback();
1509  $this->error = $this->db->lasterror();
1510  return -1;
1511  }
1512  } else {
1513  dol_syslog(get_class($this)."::setApproved expensereport already with approve status", LOG_WARNING);
1514  }
1515 
1516  return 0;
1517  }
1518 
1527  public function setDeny($fuser, $details, $notrigger = 0)
1528  {
1529  $now = dol_now();
1530  $error = 0;
1531 
1532  // date de refus
1533  if ($this->status != self::STATUS_REFUSED) {
1534  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1535  $sql .= " SET ref = '".$this->db->escape($this->ref)."', fk_statut = ".self::STATUS_REFUSED.", fk_user_refuse = ".((int) $fuser->id).",";
1536  $sql .= " date_refuse='".$this->db->idate($now)."',";
1537  $sql .= " detail_refuse='".$this->db->escape($details)."',";
1538  $sql .= " fk_user_approve = NULL";
1539  $sql .= " WHERE rowid = ".((int) $this->id);
1540  if ($this->db->query($sql)) {
1541  $this->fk_statut = 99; // deprecated
1542  $this->status = 99;
1543  $this->fk_user_refuse = $fuser->id;
1544  $this->detail_refuse = $details;
1545  $this->date_refuse = $now;
1546 
1547  if (!$notrigger) {
1548  // Call trigger
1549  $result = $this->call_trigger('EXPENSE_REPORT_DENY', $fuser);
1550 
1551  if ($result < 0) {
1552  $error++;
1553  }
1554  // End call triggers
1555  }
1556 
1557  if (empty($error)) {
1558  $this->db->commit();
1559  return 1;
1560  } else {
1561  $this->db->rollback();
1562  $this->error = $this->db->error();
1563  return -2;
1564  }
1565  } else {
1566  $this->db->rollback();
1567  $this->error = $this->db->lasterror();
1568  return -1;
1569  }
1570  } else {
1571  dol_syslog(get_class($this)."::setDeny expensereport already with refuse status", LOG_WARNING);
1572  }
1573 
1574  return 0;
1575  }
1576 
1577  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1587  public function set_unpaid($fuser, $notrigger = 0)
1588  {
1589  // phpcs:enable
1590  dol_syslog(get_class($this)."::set_unpaid is deprecated, use setUnpaid instead", LOG_NOTICE);
1591  return $this->setUnpaid($fuser, $notrigger);
1592  }
1593 
1601  public function setUnpaid($fuser, $notrigger = 0)
1602  {
1603  $error = 0;
1604 
1605  if ($this->paid) {
1606  $this->db->begin();
1607 
1608  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1609  $sql .= " SET paid = 0, fk_statut = ".self::STATUS_APPROVED;
1610  $sql .= " WHERE rowid = ".((int) $this->id);
1611 
1612  dol_syslog(get_class($this)."::set_unpaid", LOG_DEBUG);
1613 
1614  if ($this->db->query($sql)) {
1615  if (!$notrigger) {
1616  // Call trigger
1617  $result = $this->call_trigger('EXPENSE_REPORT_UNPAID', $fuser);
1618 
1619  if ($result < 0) {
1620  $error++;
1621  }
1622  // End call triggers
1623  }
1624 
1625  if (empty($error)) {
1626  $this->db->commit();
1627  return 1;
1628  } else {
1629  $this->db->rollback();
1630  $this->error = $this->db->error();
1631  return -2;
1632  }
1633  } else {
1634  $this->db->rollback();
1635  $this->error = $this->db->error();
1636  return -1;
1637  }
1638  } else {
1639  dol_syslog(get_class($this)."::set_unpaid expensereport already with unpaid status", LOG_WARNING);
1640  }
1641 
1642  return 0;
1643  }
1644 
1645  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1654  public function set_cancel($fuser, $detail, $notrigger = 0)
1655  {
1656  // phpcs:enable
1657  $error = 0;
1658  $this->date_cancel = $this->db->idate(dol_now());
1659  if ($this->status != self::STATUS_CANCELED) {
1660  $this->db->begin();
1661 
1662  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1663  $sql .= " SET fk_statut = ".self::STATUS_CANCELED.", fk_user_cancel = ".((int) $fuser->id);
1664  $sql .= ", date_cancel='".$this->db->idate($this->date_cancel)."'";
1665  $sql .= ", detail_cancel='".$this->db->escape($detail)."'";
1666  $sql .= " WHERE rowid = ".((int) $this->id);
1667 
1668  dol_syslog(get_class($this)."::set_cancel", LOG_DEBUG);
1669 
1670  if ($this->db->query($sql)) {
1671  if (!$notrigger) {
1672  // Call trigger
1673  $result = $this->call_trigger('EXPENSE_REPORT_CANCEL', $fuser);
1674 
1675  if ($result < 0) {
1676  $error++;
1677  }
1678  // End call triggers
1679  }
1680 
1681  if (empty($error)) {
1682  $this->db->commit();
1683  return 1;
1684  } else {
1685  $this->db->rollback();
1686  $this->error = $this->db->error();
1687  return -2;
1688  }
1689  } else {
1690  $this->db->rollback();
1691  $this->error = $this->db->error();
1692  return -1;
1693  }
1694  } else {
1695  dol_syslog(get_class($this)."::set_cancel expensereport already with cancel status", LOG_WARNING);
1696  }
1697  return 0;
1698  }
1699 
1705  public function getNextNumRef()
1706  {
1707  global $langs, $conf;
1708  $langs->load("trips");
1709 
1710  if (getDolGlobalString('EXPENSEREPORT_ADDON')) {
1711  $mybool = false;
1712 
1713  $file = getDolGlobalString('EXPENSEREPORT_ADDON') . ".php";
1714  $classname = getDolGlobalString('EXPENSEREPORT_ADDON');
1715 
1716  // Include file with class
1717  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
1718  foreach ($dirmodels as $reldir) {
1719  $dir = dol_buildpath($reldir."core/modules/expensereport/");
1720 
1721  // Load file with numbering class (if found)
1722  $mybool = ((bool) @include_once $dir.$file) || $mybool;
1723  }
1724 
1725  if (!$mybool) {
1726  dol_print_error(null, "Failed to include file ".$file);
1727  return '';
1728  }
1729 
1730  $obj = new $classname();
1731  $numref = $obj->getNextValue($this);
1732 
1733  if ($numref != "") {
1734  return $numref;
1735  } else {
1736  $this->error = $obj->error;
1737  $this->errors = $obj->errors;
1738  //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
1739  return -1;
1740  }
1741  } else {
1742  $this->error = "Error_EXPENSEREPORT_ADDON_NotDefined";
1743  return -2;
1744  }
1745  }
1746 
1754  public function getTooltipContentArray($params)
1755  {
1756  global $conf, $langs;
1757 
1758  $langs->load('trips');
1759 
1760  $nofetch = !empty($params['nofetch']);
1761  $moretitle = $params['moretitle'] ?? '';
1762 
1763  $datas = array();
1764  $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("ExpenseReport").'</u>';
1765  if (isset($this->status)) {
1766  $datas['picto'] .= ' '.$this->getLibStatut(5);
1767  }
1768  if ($moretitle) {
1769  $datas['picto'] .= ' - '.$moretitle;
1770  }
1771  if (!empty($this->ref)) {
1772  $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1773  }
1774  if (!empty($this->total_ht)) {
1775  $datas['total_ht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
1776  }
1777  if (!empty($this->total_tva)) {
1778  $datas['total_tva'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
1779  }
1780  if (!empty($this->total_ttc)) {
1781  $datas['total_ttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
1782  }
1783 
1784  return $datas;
1785  }
1786 
1799  public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $moretitle = '', $notooltip = 0, $save_lastsearch_value = -1)
1800  {
1801  global $langs, $hookmanager;
1802 
1803  $result = '';
1804 
1805  $url = DOL_URL_ROOT.'/expensereport/card.php?id='.$this->id;
1806 
1807  if ($short) {
1808  return $url;
1809  }
1810 
1811  $params = [
1812  'id' => $this->id,
1813  'objecttype' => $this->element,
1814  'option' => $option,
1815  'moretitle' => $moretitle,
1816  'nofetch' => 1,
1817  ];
1818  $classfortooltip = 'classfortooltip';
1819  $dataparams = '';
1820  if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1821  $classfortooltip = 'classforajaxtooltip';
1822  $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
1823  $label = '';
1824  } else {
1825  $label = implode($this->getTooltipContentArray($params));
1826  }
1827 
1828  if ($option != 'nolink') {
1829  // Add param to save lastsearch_values or not
1830  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1831  if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1832  $add_save_lastsearch_values = 1;
1833  }
1834  if ($add_save_lastsearch_values) {
1835  $url .= '&save_lastsearch_values=1';
1836  }
1837  }
1838 
1839  $ref = $this->ref;
1840  if (empty($ref)) {
1841  $ref = $this->id;
1842  }
1843 
1844  $linkclose = '';
1845  if (empty($notooltip)) {
1846  if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1847  $label = $langs->trans("ShowExpenseReport");
1848  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1849  }
1850  $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
1851  $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
1852  }
1853 
1854  $linkstart = '<a href="'.$url.'"';
1855  $linkstart .= $linkclose.'>';
1856  $linkend = '</a>';
1857 
1858  $result .= $linkstart;
1859  if ($withpicto) {
1860  $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'"'), 0, 0, $notooltip ? 0 : 1);
1861  }
1862  if ($withpicto != 2) {
1863  $result .= ($max ? dol_trunc($ref, $max) : $ref);
1864  }
1865  $result .= $linkend;
1866 
1867  global $action;
1868  $hookmanager->initHooks(array($this->element . 'dao'));
1869  $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1870  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1871  if ($reshook > 0) {
1872  $result = $hookmanager->resPrint;
1873  } else {
1874  $result .= $hookmanager->resPrint;
1875  }
1876  return $result;
1877  }
1878 
1879  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1887  public function update_totaux_add($ligne_total_ht, $ligne_total_tva)
1888  {
1889  // phpcs:enable
1890  $this->total_ht += (float) $ligne_total_ht;
1891  $this->total_tva += (float) $ligne_total_tva;
1892  $this->total_ttc += $this->total_tva;
1893 
1894  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
1895  $sql .= " total_ht = ".$this->total_ht;
1896  $sql .= " , total_ttc = ".$this->total_ttc;
1897  $sql .= " , total_tva = ".$this->total_tva;
1898  $sql .= " WHERE rowid = ".((int) $this->id);
1899 
1900  $result = $this->db->query($sql);
1901  if ($result) {
1902  return 1;
1903  } else {
1904  $this->error = $this->db->error();
1905  return -1;
1906  }
1907  }
1908 
1924  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)
1925  {
1926  global $langs, $mysoc;
1927 
1928  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);
1929 
1930  if ($this->status == self::STATUS_DRAFT) {
1931  if (empty($qty)) {
1932  $qty = 0;
1933  }
1934  if (empty($fk_c_type_fees) || $fk_c_type_fees < 0) {
1935  $fk_c_type_fees = 0;
1936  }
1937  if (empty($fk_c_exp_tax_cat) || $fk_c_exp_tax_cat < 0) {
1938  $fk_c_exp_tax_cat = 0;
1939  }
1940  if (empty($vatrate) || $vatrate < 0) {
1941  $vatrate = 0;
1942  }
1943  if (empty($date)) {
1944  $date = '';
1945  }
1946  if (empty($fk_project)) {
1947  $fk_project = 0;
1948  }
1949 
1950  $qty = (float) price2num($qty);
1951  if (!preg_match('/\s*\‍((.*)\‍)/', $vatrate)) {
1952  $vatrate = price2num($vatrate); // $txtva can have format '5.0 (XXX)' or '5'
1953  }
1954  $up = price2num($up);
1955 
1956  $this->db->begin();
1957 
1958  $this->line = new ExpenseReportLine($this->db);
1959 
1960  // We don't know seller and buyer for expense reports
1961  $seller = $mysoc; // We use same than current company (expense report are often done in same country)
1962  $seller->tva_assuj = 1; // Most seller uses vat
1963  $buyer = new Societe($this->db);
1964 
1965  $localtaxes_type = getLocalTaxesFromRate($vatrate, 0, $buyer, $seller);
1966 
1967  $vat_src_code = '';
1968  $reg = array();
1969  if (preg_match('/\s*\‍((.*)\‍)/', $vatrate, $reg)) {
1970  $vat_src_code = $reg[1];
1971  $vatrate = preg_replace('/\s*\‍(.*\‍)/', '', $vatrate); // Remove code into vatrate.
1972  }
1973  $vatrate = preg_replace('/\*/', '', $vatrate);
1974 
1975  $tmp = calcul_price_total($qty, $up, 0, $vatrate, -1, -1, 0, 'TTC', 0, $type, $seller, $localtaxes_type);
1976 
1977  $this->line->value_unit = $up;
1978 
1979  $this->line->vat_src_code = $vat_src_code;
1980  $this->line->vatrate = price2num($vatrate);
1981  $this->line->localtax1_tx = $localtaxes_type[1];
1982  $this->line->localtax2_tx = $localtaxes_type[3];
1983  $this->line->localtax1_type = $localtaxes_type[0];
1984  $this->line->localtax2_type = $localtaxes_type[2];
1985 
1986  $this->line->total_ttc = $tmp[2];
1987  $this->line->total_ht = $tmp[0];
1988  $this->line->total_tva = $tmp[1];
1989  $this->line->total_localtax1 = $tmp[9];
1990  $this->line->total_localtax2 = $tmp[10];
1991 
1992  $this->line->fk_expensereport = $this->id;
1993  $this->line->qty = $qty;
1994  $this->line->date = $date;
1995  $this->line->fk_c_type_fees = $fk_c_type_fees;
1996  $this->line->fk_c_exp_tax_cat = $fk_c_exp_tax_cat;
1997  $this->line->comments = $comments;
1998  $this->line->fk_projet = $fk_project; // deprecated
1999  $this->line->fk_project = $fk_project;
2000 
2001  $this->line->fk_ecm_files = $fk_ecm_files;
2002 
2003  $this->applyOffset();
2004  $this->checkRules($type, $seller);
2005 
2006  $result = $this->line->insert(0, true);
2007  if ($result > 0) {
2008  $result = $this->update_price(1); // This method is designed to add line from user input so total calculation must be done using 'auto' mode.
2009  if ($result > 0) {
2010  $this->db->commit();
2011  return $this->line->id;
2012  } else {
2013  $this->db->rollback();
2014  return -1;
2015  }
2016  } else {
2017  $this->error = $this->line->error;
2018  dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
2019  $this->db->rollback();
2020  return -2;
2021  }
2022  } else {
2023  dol_syslog(get_class($this)."::addline status of expense report must be Draft to allow use of ->addline()", LOG_ERR);
2024  $this->error = 'ErrorExpenseNotDraft';
2025  return -3;
2026  }
2027  }
2028 
2036  public function checkRules($type = 0, $seller = '')
2037  {
2038  global $conf, $db, $langs, $mysoc;
2039 
2040  $langs->load('trips');
2041 
2042  // We don't know seller and buyer for expense reports
2043  if (!is_object($seller)) {
2044  $seller = $mysoc; // We use same than current company (expense report are often done in same country)
2045  $seller->tva_assuj = 1; // Most seller uses vat
2046  }
2047 
2048  $expensereportrule = new ExpenseReportRule($db);
2049  $rulestocheck = $expensereportrule->getAllRule($this->line->fk_c_type_fees, $this->line->date, $this->fk_user_author);
2050 
2051  $violation = 0;
2052  $rule_warning_message_tab = array();
2053 
2054  $current_total_ttc = $this->line->total_ttc;
2055  $new_current_total_ttc = $this->line->total_ttc;
2056 
2057  // check if one is violated
2058  foreach ($rulestocheck as $rule) {
2059  if (in_array($rule->code_expense_rules_type, array('EX_DAY', 'EX_MON', 'EX_YEA'))) {
2060  $amount_to_test = $this->line->getExpAmount($rule, $this->fk_user_author, $rule->code_expense_rules_type);
2061  } else {
2062  $amount_to_test = $current_total_ttc; // EX_EXP
2063  }
2064 
2065  $amount_to_test = $amount_to_test - $current_total_ttc + $new_current_total_ttc; // if amount as been modified by a previous rule
2066 
2067  if ($amount_to_test > $rule->amount) {
2068  $violation++;
2069 
2070  if ($rule->restrictive) {
2071  $this->error = 'ExpenseReportConstraintViolationError';
2072  $this->errors[] = $this->error;
2073 
2074  $new_current_total_ttc -= $amount_to_test - $rule->amount; // ex, entered 16€, limit 12€, subtracts 4€;
2075  $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));
2076  } else {
2077  $this->error = 'ExpenseReportConstraintViolationWarning';
2078  $this->errors[] = $this->error;
2079 
2080  $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));
2081  }
2082 
2083  // No break, we should test if another rule is violated
2084  }
2085  }
2086 
2087  $this->line->rule_warning_message = implode('\n', $rule_warning_message_tab);
2088 
2089  if ($violation > 0) {
2090  $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);
2091 
2092  $this->line->value_unit = $tmp[5];
2093  $this->line->total_ttc = $tmp[2];
2094  $this->line->total_ht = $tmp[0];
2095  $this->line->total_tva = $tmp[1];
2096  $this->line->total_localtax1 = $tmp[9];
2097  $this->line->total_localtax2 = $tmp[10];
2098 
2099  return false;
2100  } else {
2101  return true;
2102  }
2103  }
2104 
2112  public function applyOffset($type = 0, $seller = '')
2113  {
2114  global $mysoc;
2115 
2116  if (!getDolGlobalString('MAIN_USE_EXPENSE_IK')) {
2117  return false;
2118  }
2119 
2120  $userauthor = new User($this->db);
2121  if ($userauthor->fetch($this->fk_user_author) <= 0) {
2122  $this->error = 'ErrorCantFetchUser';
2123  $this->errors[] = 'ErrorCantFetchUser';
2124  return false;
2125  }
2126 
2127  // We don't know seller and buyer for expense reports
2128  if (!is_object($seller)) {
2129  $seller = $mysoc; // We use same than current company (expense report are often done in same country)
2130  $seller->tva_assuj = 1; // Most seller uses vat
2131  }
2132 
2133  $expenseik = new ExpenseReportIk($this->db);
2134  $range = $expenseik->getRangeByUser($userauthor, $this->line->fk_c_exp_tax_cat);
2135 
2136  if (empty($range)) {
2137  $this->error = 'ErrorNoRangeAvailable';
2138  $this->errors[] = 'ErrorNoRangeAvailable';
2139  return false;
2140  }
2141 
2142  if (getDolGlobalString('MAIN_EXPENSE_APPLY_ENTIRE_OFFSET')) {
2143  $ikoffset = $range->ikoffset;
2144  } else {
2145  $ikoffset = $range->ikoffset / 12; // The amount of offset is a global value for the year
2146  }
2147 
2148  // Test if ikoffset has been applied for the current month
2149  if (!$this->offsetAlreadyGiven()) {
2150  $new_up = $range->coef + ($ikoffset / $this->line->qty);
2151  $tmp = calcul_price_total($this->line->qty, $new_up, 0, $this->line->vatrate, 0, 0, 0, 'TTC', 0, $type, $seller);
2152 
2153  $this->line->value_unit = $tmp[5];
2154  $this->line->total_ttc = $tmp[2];
2155  $this->line->total_ht = $tmp[0];
2156  $this->line->total_tva = $tmp[1];
2157  $this->line->total_localtax1 = $tmp[9];
2158  $this->line->total_localtax2 = $tmp[10];
2159 
2160  return true;
2161  }
2162 
2163  return false;
2164  }
2165 
2171  public function offsetAlreadyGiven()
2172  {
2173  $sql = 'SELECT e.rowid FROM '.MAIN_DB_PREFIX.'expensereport e';
2174  $sql .= " INNER JOIN ".MAIN_DB_PREFIX."expensereport_det d ON (e.rowid = d.fk_expensereport)";
2175  $sql .= " INNER JOIN ".MAIN_DB_PREFIX."c_type_fees f ON (d.fk_c_type_fees = f.id AND f.code = 'EX_KME')";
2176  $sql .= " WHERE e.fk_user_author = ".(int) $this->fk_user_author;
2177  $sql .= " AND YEAR(d.date) = '".dol_print_date($this->line->date, '%Y')."' AND MONTH(d.date) = '".dol_print_date($this->line->date, '%m')."'";
2178  if (!empty($this->line->id)) {
2179  $sql .= ' AND d.rowid <> '.((int) $this->line->id);
2180  }
2181 
2182  dol_syslog(get_class($this)."::offsetAlreadyGiven");
2183  $resql = $this->db->query($sql);
2184  if ($resql) {
2185  $num = $this->db->num_rows($resql);
2186  if ($num > 0) {
2187  return true;
2188  }
2189  } else {
2190  dol_print_error($this->db);
2191  }
2192 
2193  return false;
2194  }
2195 
2213  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)
2214  {
2215  global $user, $mysoc;
2216 
2217  if ($this->status == self::STATUS_DRAFT || $this->status == self::STATUS_REFUSED) {
2218  $this->db->begin();
2219 
2220  $error = 0;
2221  $type = 0; // TODO What if type is service ?
2222 
2223  // We don't know seller and buyer for expense reports
2224  $seller = $mysoc; // We use same than current company (expense report are often done in same country)
2225  $seller->tva_assuj = 1; // Most seller uses vat
2226  $seller->localtax1_assuj = $mysoc->localtax1_assuj; // We don't know, we reuse the state of company
2227  $seller->localtax2_assuj = $mysoc->localtax1_assuj; // We don't know, we reuse the state of company
2228  $buyer = new Societe($this->db);
2229 
2230  $localtaxes_type = getLocalTaxesFromRate($vatrate, 0, $buyer, $seller);
2231 
2232  // Clean vat code
2233  $reg = array();
2234  $vat_src_code = '';
2235  if (preg_match('/\‍((.*)\‍)/', (string) $vatrate, $reg)) {
2236  $vat_src_code = $reg[1];
2237  $vatrate = preg_replace('/\s*\‍(.*\‍)/', '', (string) $vatrate); // Remove code into vatrate.
2238  }
2239  $vatrate = preg_replace('/\*/', '', $vatrate);
2240 
2241  $tmp = calcul_price_total($qty, $value_unit, 0, $vatrate, -1, -1, 0, 'TTC', 0, $type, $seller, $localtaxes_type);
2242  //var_dump($vatrate);var_dump($localtaxes_type);var_dump($tmp);exit;
2243  // calcul total of line
2244  //$total_ttc = price2num($qty*$value_unit, 'MT');
2245 
2246  $tx_tva = 1 + (float) $vatrate / 100;
2247 
2248  $this->line = new ExpenseReportLine($this->db);
2249  $this->line->comments = $comments;
2250  $this->line->qty = $qty;
2251  $this->line->value_unit = $value_unit;
2252  $this->line->date = $date;
2253 
2254  $this->line->fk_expensereport = $expensereport_id;
2255  $this->line->fk_c_type_fees = $type_fees_id;
2256  $this->line->fk_c_exp_tax_cat = $fk_c_exp_tax_cat;
2257  $this->line->fk_projet = $projet_id; // deprecated
2258  $this->line->fk_project = $projet_id;
2259 
2260  $this->line->vat_src_code = $vat_src_code;
2261  $this->line->vatrate = price2num($vatrate);
2262  $this->line->localtax1_tx = $localtaxes_type[1];
2263  $this->line->localtax2_tx = $localtaxes_type[3];
2264  $this->line->localtax1_type = $localtaxes_type[0];
2265  $this->line->localtax2_type = $localtaxes_type[2];
2266 
2267  $this->line->total_ttc = $tmp[2];
2268  $this->line->total_ht = $tmp[0];
2269  $this->line->total_tva = $tmp[1];
2270  $this->line->total_localtax1 = $tmp[9];
2271  $this->line->total_localtax2 = $tmp[10];
2272 
2273  $this->line->fk_ecm_files = $fk_ecm_files;
2274 
2275  $this->line->id = ((int) $rowid);
2276 
2277  // Select des infos sur le type fees
2278  $sql = "SELECT c.code as code_type_fees, c.label as label_type_fees";
2279  $sql .= " FROM ".MAIN_DB_PREFIX."c_type_fees as c";
2280  $sql .= " WHERE c.id = ".((int) $type_fees_id);
2281  $resql = $this->db->query($sql);
2282  if ($resql) {
2283  $objp_fees = $this->db->fetch_object($resql);
2284  $this->line->type_fees_code = $objp_fees->code_type_fees;
2285  $this->line->type_fees_libelle = $objp_fees->label_type_fees;
2286  $this->db->free($resql);
2287  }
2288 
2289  // Select des information du projet
2290  $sql = "SELECT p.ref as ref_projet, p.title as title_projet";
2291  $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
2292  $sql .= " WHERE p.rowid = ".((int) $projet_id);
2293  $resql = $this->db->query($sql);
2294  if ($resql) {
2295  $objp_projet = $this->db->fetch_object($resql);
2296  $this->line->projet_ref = $objp_projet->ref_projet;
2297  $this->line->projet_title = $objp_projet->title_projet;
2298  $this->db->free($resql);
2299  }
2300 
2301  $this->applyOffset();
2302  $this->checkRules();
2303 
2304  $result = $this->line->update($user);
2305  if ($result < 0) {
2306  $error++;
2307  }
2308 
2309  if (!$error && !$notrigger) {
2310  // Call triggers
2311  $result = $this->call_trigger('EXPENSE_REPORT_DET_MODIFY', $user);
2312  if ($result < 0) {
2313  $error++;
2314  }
2315  // End call triggers
2316  }
2317 
2318  if (!$error) {
2319  $this->db->commit();
2320  return 1;
2321  } else {
2322  $this->error = $this->line->error;
2323  $this->errors = $this->line->errors;
2324  $this->db->rollback();
2325  return -2;
2326  }
2327  }
2328 
2329  return 0;
2330  }
2331 
2340  public function deleteLine($rowid, $fuser = '', $notrigger = 0)
2341  {
2342  $error = 0;
2343 
2344  $this->db->begin();
2345 
2346  if (!$notrigger) {
2347  // Call triggers
2348  $result = $this->call_trigger('EXPENSE_REPORT_DET_DELETE', $fuser);
2349  if ($result < 0) {
2350  $error++;
2351  }
2352  // End call triggers
2353  }
2354 
2355  $sql = ' DELETE FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2356  $sql .= ' WHERE rowid = '.((int) $rowid);
2357 
2358  dol_syslog(get_class($this)."::deleteline sql=".$sql);
2359  $result = $this->db->query($sql);
2360 
2361  if (!$result || $error > 0) {
2362  $this->error = $this->db->error();
2363  dol_syslog(get_class($this)."::deleteline Error ".$this->error, LOG_ERR);
2364  $this->db->rollback();
2365  return -1;
2366  }
2367 
2368  $this->update_price(1);
2369 
2370  $this->db->commit();
2371 
2372  return 1;
2373  }
2374 
2375  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2384  public function periode_existe($fuser, $date_debut, $date_fin)
2385  {
2386  global $conf;
2387 
2388  // phpcs:enable
2389  $sql = "SELECT rowid, date_debut, date_fin";
2390  $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element;
2391  $sql .= " WHERE entity = ".((int) $conf->entity); // not shared, only for the current entity
2392  $sql .= " AND fk_user_author = ".((int) $fuser->id);
2393 
2394  dol_syslog(get_class($this)."::periode_existe sql=".$sql);
2395  $result = $this->db->query($sql);
2396  if ($result) {
2397  $num_rows = $this->db->num_rows($result);
2398  $i = 0;
2399 
2400  if ($num_rows > 0) {
2401  $date_d_form = $date_debut;
2402  $date_f_form = $date_fin;
2403 
2404  while ($i < $num_rows) {
2405  $objp = $this->db->fetch_object($result);
2406 
2407  $date_d_req = $this->db->jdate($objp->date_debut); // 3
2408  $date_f_req = $this->db->jdate($objp->date_fin); // 4
2409 
2410  if (!($date_f_form < $date_d_req || $date_d_form > $date_f_req)) {
2411  return $objp->rowid;
2412  }
2413 
2414  $i++;
2415  }
2416 
2417  return 0;
2418  } else {
2419  return 0;
2420  }
2421  } else {
2422  $this->error = $this->db->lasterror();
2423  dol_syslog(get_class($this)."::periode_existe Error ".$this->error, LOG_ERR);
2424  return -1;
2425  }
2426  }
2427 
2428 
2429  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2437  {
2438  // phpcs:enable
2439  $users_validator = array();
2440 
2441  $sql = "SELECT DISTINCT ur.fk_user";
2442  $sql .= " FROM ".MAIN_DB_PREFIX."user_rights as ur, ".MAIN_DB_PREFIX."rights_def as rd";
2443  $sql .= " WHERE ur.fk_id = rd.id and rd.module = 'expensereport' AND rd.perms = 'approve'"; // Permission 'Approve';
2444  $sql .= " UNION";
2445  $sql .= " SELECT DISTINCT ugu.fk_user";
2446  $sql .= " FROM ".MAIN_DB_PREFIX."usergroup_user as ugu, ".MAIN_DB_PREFIX."usergroup_rights as ur, ".MAIN_DB_PREFIX."rights_def as rd";
2447  $sql .= " WHERE ugu.fk_usergroup = ur.fk_usergroup AND ur.fk_id = rd.id and rd.module = 'expensereport' AND rd.perms = 'approve'"; // Permission 'Approve';
2448  //print $sql;
2449 
2450  dol_syslog(get_class($this)."::fetch_users_approver_expensereport sql=".$sql);
2451  $result = $this->db->query($sql);
2452  if ($result) {
2453  $num_rows = $this->db->num_rows($result);
2454  $i = 0;
2455  while ($i < $num_rows) {
2456  $objp = $this->db->fetch_object($result);
2457  array_push($users_validator, $objp->fk_user);
2458  $i++;
2459  }
2460  return $users_validator;
2461  } else {
2462  $this->error = $this->db->lasterror();
2463  dol_syslog(get_class($this)."::fetch_users_approver_expensereport Error ".$this->error, LOG_ERR);
2464  return -1;
2465  }
2466  }
2467 
2479  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2480  {
2481  $outputlangs->load("trips");
2482 
2483  if (!dol_strlen($modele)) {
2484  if (!empty($this->model_pdf)) {
2485  $modele = $this->model_pdf;
2486  } elseif (getDolGlobalString('EXPENSEREPORT_ADDON_PDF')) {
2487  $modele = getDolGlobalString('EXPENSEREPORT_ADDON_PDF');
2488  }
2489  }
2490 
2491  if (!empty($modele)) {
2492  $modelpath = "core/modules/expensereport/doc/";
2493 
2494  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2495  } else {
2496  return 0;
2497  }
2498  }
2499 
2506  public function listOfTypes($active = 1)
2507  {
2508  global $langs;
2509  $ret = array();
2510  $sql = "SELECT id, code, label";
2511  $sql .= " FROM ".MAIN_DB_PREFIX."c_type_fees";
2512  $sql .= " WHERE active = ".((int) $active);
2513  dol_syslog(get_class($this)."::listOfTypes", LOG_DEBUG);
2514  $result = $this->db->query($sql);
2515  if ($result) {
2516  $num = $this->db->num_rows($result);
2517  $i = 0;
2518  while ($i < $num) {
2519  $obj = $this->db->fetch_object($result);
2520  $ret[$obj->code] = (($langs->transnoentitiesnoconv($obj->code) != $obj->code) ? $langs->transnoentitiesnoconv($obj->code) : $obj->label);
2521  $i++;
2522  }
2523  } else {
2524  dol_print_error($this->db);
2525  }
2526  return $ret;
2527  }
2528 
2534  public function loadStateBoard()
2535  {
2536  global $user;
2537 
2538  $this->nb = array();
2539 
2540  $sql = "SELECT count(ex.rowid) as nb";
2541  $sql .= " FROM ".MAIN_DB_PREFIX."expensereport as ex";
2542  $sql .= " WHERE ex.fk_statut > 0";
2543  $sql .= " AND ex.entity IN (".getEntity('expensereport').")";
2544  if (!$user->hasRight('expensereport', 'readall')) {
2545  $userchildids = $user->getAllChildIds(1);
2546  $sql .= " AND (ex.fk_user_author IN (".$this->db->sanitize(implode(',', $userchildids)).")";
2547  $sql .= " OR ex.fk_user_validator IN (".$this->db->sanitize(implode(',', $userchildids))."))";
2548  }
2549 
2550  $resql = $this->db->query($sql);
2551  if ($resql) {
2552  while ($obj = $this->db->fetch_object($resql)) {
2553  $this->nb["expensereports"] = $obj->nb;
2554  }
2555  $this->db->free($resql);
2556  return 1;
2557  } else {
2558  dol_print_error($this->db);
2559  $this->error = $this->db->error();
2560  return -1;
2561  }
2562  }
2563 
2564  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2572  public function load_board($user, $option = 'topay')
2573  {
2574  // phpcs:enable
2575  global $conf, $langs;
2576 
2577  if ($user->socid) {
2578  return -1; // protection pour eviter appel par utilisateur externe
2579  }
2580 
2581  $now = dol_now();
2582 
2583  $sql = "SELECT ex.rowid, ex.date_valid";
2584  $sql .= " FROM ".MAIN_DB_PREFIX."expensereport as ex";
2585  if ($option == 'toapprove') {
2586  $sql .= " WHERE ex.fk_statut = ".self::STATUS_VALIDATED;
2587  } else {
2588  $sql .= " WHERE ex.fk_statut = ".self::STATUS_APPROVED;
2589  }
2590  $sql .= " AND ex.entity IN (".getEntity('expensereport').")";
2591  if (!$user->hasRight('expensereport', 'readall')) {
2592  $userchildids = $user->getAllChildIds(1);
2593  $sql .= " AND (ex.fk_user_author IN (".$this->db->sanitize(implode(',', $userchildids)).")";
2594  $sql .= " OR ex.fk_user_validator IN (".$this->db->sanitize(implode(',', $userchildids))."))";
2595  }
2596 
2597  $resql = $this->db->query($sql);
2598  if ($resql) {
2599  $langs->load("trips");
2600 
2601  $response = new WorkboardResponse();
2602  if ($option == 'toapprove') {
2603  $response->warning_delay = $conf->expensereport->approve->warning_delay / 60 / 60 / 24;
2604  $response->label = $langs->trans("ExpenseReportsToApprove");
2605  $response->labelShort = $langs->trans("ToApprove");
2606  $response->url = DOL_URL_ROOT.'/expensereport/list.php?mainmenu=hrm&amp;statut='.self::STATUS_VALIDATED;
2607  } else {
2608  $response->warning_delay = $conf->expensereport->payment->warning_delay / 60 / 60 / 24;
2609  $response->label = $langs->trans("ExpenseReportsToPay");
2610  $response->labelShort = $langs->trans("StatusToPay");
2611  $response->url = DOL_URL_ROOT.'/expensereport/list.php?mainmenu=hrm&amp;statut='.self::STATUS_APPROVED;
2612  }
2613  $response->img = img_object('', "trip");
2614 
2615  while ($obj = $this->db->fetch_object($resql)) {
2616  $response->nbtodo++;
2617 
2618  if ($option == 'toapprove') {
2619  if ($this->db->jdate($obj->date_valid) < ($now - $conf->expensereport->approve->warning_delay)) {
2620  $response->nbtodolate++;
2621  }
2622  } else {
2623  if ($this->db->jdate($obj->date_valid) < ($now - $conf->expensereport->payment->warning_delay)) {
2624  $response->nbtodolate++;
2625  }
2626  }
2627  }
2628 
2629  return $response;
2630  } else {
2631  dol_print_error($this->db);
2632  $this->error = $this->db->error();
2633  return -1;
2634  }
2635  }
2636 
2643  public function hasDelay($option)
2644  {
2645  global $conf;
2646 
2647  // Only valid expenses reports
2648  if ($option == 'toapprove' && $this->status != 2) {
2649  return false;
2650  }
2651  if ($option == 'topay' && $this->status != 5) {
2652  return false;
2653  }
2654 
2655  $now = dol_now();
2656  if ($option == 'toapprove') {
2657  return (!empty($this->datevalid) ? $this->datevalid : $this->date_valid) < ($now - $conf->expensereport->approve->warning_delay);
2658  } else {
2659  return (!empty($this->datevalid) ? $this->datevalid : $this->date_valid) < ($now - $conf->expensereport->payment->warning_delay);
2660  }
2661  }
2662 
2668  public function getVentilExportCompta()
2669  {
2670  $alreadydispatched = 0;
2671 
2672  $type = 'expense_report';
2673 
2674  $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);
2675  $resql = $this->db->query($sql);
2676  if ($resql) {
2677  $obj = $this->db->fetch_object($resql);
2678  if ($obj) {
2679  $alreadydispatched = $obj->nb;
2680  }
2681  } else {
2682  $this->error = $this->db->lasterror();
2683  return -1;
2684  }
2685 
2686  if ($alreadydispatched) {
2687  return 1;
2688  }
2689  return 0;
2690  }
2691 
2697  public function getSumPayments()
2698  {
2699  $table = 'payment_expensereport';
2700  $field = 'fk_expensereport';
2701 
2702  $sql = 'SELECT sum(amount) as amount';
2703  $sql .= ' FROM '.MAIN_DB_PREFIX.$table;
2704  $sql .= " WHERE ".$field." = ".((int) $this->id);
2705 
2706  dol_syslog(get_class($this)."::getSumPayments", LOG_DEBUG);
2707  $resql = $this->db->query($sql);
2708  if ($resql) {
2709  $obj = $this->db->fetch_object($resql);
2710  $this->db->free($resql);
2711  return (empty($obj->amount) ? 0 : $obj->amount);
2712  } else {
2713  $this->error = $this->db->lasterror();
2714  return -1;
2715  }
2716  }
2717 
2726  public function computeTotalKm($fk_cat, $qty, $tva)
2727  {
2728  global $langs, $db, $conf;
2729 
2730  $cumulYearQty = 0;
2731  $ranges = array();
2732  $coef = 0;
2733 
2734 
2735  if ($fk_cat < 0) {
2736  $this->error = $langs->trans('ErrorBadParameterCat');
2737  return -1;
2738  }
2739 
2740  if ($qty <= 0) {
2741  $this->error = $langs->trans('ErrorBadParameterQty');
2742  return -1;
2743  }
2744 
2745  $currentUser = new User($db);
2746  $currentUser->fetch($this->fk_user);
2747  $currentUser->getrights('expensereport');
2748  //Clean
2749  $qty = (float) price2num($qty);
2750 
2751  $sql = " SELECT r.range_ik, t.ikoffset, t.coef";
2752  $sql .= " FROM ".MAIN_DB_PREFIX."expensereport_ik t";
2753  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_exp_tax_range r ON r.rowid = t.fk_range";
2754  $sql .= " WHERE t.fk_c_exp_tax_cat = ".(int) $fk_cat;
2755  $sql .= " ORDER BY r.range_ik ASC";
2756 
2757  dol_syslog("expenseReport::computeTotalkm sql=".$sql, LOG_DEBUG);
2758 
2759  $result = $this->db->query($sql);
2760 
2761  if ($result) {
2762  if ($conf->global->EXPENSEREPORT_CALCULATE_MILEAGE_EXPENSE_COEFFICIENT_ON_CURRENT_YEAR) {
2763  $arrayDate = dol_getdate(dol_now());
2764  $sql = " SELECT count(n.qty) as cumul FROM ".MAIN_DB_PREFIX."expensereport_det n";
2765  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."expensereport e ON e.rowid = n.fk_expensereport";
2766  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_type_fees tf ON tf.id = n.fk_c_type_fees";
2767  $sql .= " WHERE e.fk_user_author = ".(int) $this->fk_user_author;
2768  $sql .= " AND YEAR(n.date) = ".(int) $arrayDate['year'];
2769  $sql .= " AND tf.code = 'EX_KME' ";
2770  $sql .= " AND e.fk_statut = ".(int) ExpenseReport::STATUS_VALIDATED;
2771 
2772  $resql = $this->db->query($sql);
2773 
2774  if ($resql) {
2775  $obj = $this->db->fetch_object($resql);
2776  $cumulYearQty = $obj->cumul;
2777  }
2778 
2779  $qty += (float) $cumulYearQty;
2780  }
2781 
2782  $num = $this->db->num_rows($result);
2783 
2784  if ($num) {
2785  for ($i = 0; $i < $num; $i++) {
2786  $obj = $this->db->fetch_object($result);
2787 
2788  $ranges[$i] = $obj;
2789  }
2790 
2791 
2792  for ($i = 0; $i < $num; $i++) {
2793  if ($i < ($num - 1)) {
2794  if ($qty > $ranges[$i]->range_ik && $qty < $ranges[$i + 1]->range_ik) {
2795  $coef = $ranges[$i]->coef;
2796  $offset = $ranges[$i]->ikoffset;
2797  }
2798  } else {
2799  if ($qty > $ranges[$i]->range_ik) {
2800  $coef = $ranges[$i]->coef;
2801  $offset = $ranges[$i]->ikoffset;
2802  }
2803  }
2804  }
2805  $total_ht = $coef;
2806  return $total_ht;
2807  } else {
2808  $this->error = $langs->trans('TaxUndefinedForThisCategory');
2809  return 0;
2810  }
2811  } else {
2812  $this->error = $this->db->error()." sql=".$sql;
2813 
2814  return -1;
2815  }
2816  }
2817 
2825  public function getKanbanView($option = '', $arraydata = null)
2826  {
2827  global $langs;
2828 
2829  $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2830 
2831  $return = '<div class="box-flex-item box-flex-grow-zero">';
2832  $return .= '<div class="info-box info-box-sm">';
2833  $return .= '<span class="info-box-icon bg-infobox-action">';
2834  $return .= img_picto('', $this->picto);
2835  $return .= '</span>';
2836  $return .= '<div class="info-box-content">';
2837  $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref).'</span>';
2838  if ($selected >= 0) {
2839  $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
2840  }
2841  if (array_key_exists('userauthor', $arraydata)) {
2842  $return .= '<br><span class="info-box-label">'.$arraydata['userauthor']->getNomUrl(-1).'</span>';
2843  }
2844  if (property_exists($this, 'date_debut') && property_exists($this, 'date_fin')) {
2845  $return .= '<br><span class="info-box-label">'.dol_print_date($this->date_debut, 'day').'</span>';
2846  $return .= ' <span class="opacitymedium">'.$langs->trans("To").'</span> ';
2847  $return .= '<span class="info-box-label">'.dol_print_date($this->date_fin, 'day').'</span>';
2848  }
2849  if (method_exists($this, 'getLibStatut')) {
2850  $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
2851  }
2852  $return .= '</div>';
2853  $return .= '</div>';
2854  $return .= '</div>';
2855  return $return;
2856  }
2857 }
2858 
2859 
2864 {
2868  public $db;
2869 
2873  public $table_element = 'expensereport_det';
2874 
2878  public $error = '';
2879 
2883  public $rowid;
2884 
2885  public $comments;
2886 
2890  public $qty;
2891  public $value_unit;
2892  public $date;
2893 
2897  public $dates;
2898 
2902  public $fk_c_type_fees;
2903 
2907  public $fk_c_exp_tax_cat;
2908 
2912  public $fk_projet;
2913 
2917  public $fk_expensereport;
2918 
2919  public $type_fees_code;
2920  public $type_fees_libelle;
2921  public $type_fees_accountancy_code;
2922 
2923  public $projet_ref;
2924  public $projet_title;
2925  public $rang;
2926 
2927  public $vatrate;
2928  public $vat_src_code;
2929  public $tva_tx;
2930  public $localtax1_tx;
2931  public $localtax2_tx;
2932  public $localtax1_type;
2933  public $localtax2_type;
2934 
2935  public $total_ht;
2936  public $total_tva;
2937  public $total_ttc;
2938  public $total_localtax1;
2939  public $total_localtax2;
2940 
2941  // Multicurrency
2945  public $fk_multicurrency;
2946 
2950  public $multicurrency_code;
2951  public $multicurrency_tx;
2952  public $multicurrency_total_ht;
2953  public $multicurrency_total_tva;
2954  public $multicurrency_total_ttc;
2955 
2959  public $fk_ecm_files;
2960 
2961  public $rule_warning_message;
2962 
2963 
2969  public function __construct($db)
2970  {
2971  $this->db = $db;
2972  }
2973 
2980  public function fetch($rowid)
2981  {
2982  $sql = 'SELECT fde.rowid, fde.fk_expensereport, fde.fk_c_type_fees, fde.fk_c_exp_tax_cat, fde.fk_projet as fk_project, fde.date,';
2983  $sql .= ' fde.tva_tx as vatrate, fde.vat_src_code, fde.comments, fde.qty, fde.value_unit, fde.total_ht, fde.total_tva, fde.total_ttc, fde.fk_ecm_files,';
2984  $sql .= ' fde.localtax1_tx, fde.localtax2_tx, fde.localtax1_type, fde.localtax2_type, fde.total_localtax1, fde.total_localtax2, fde.rule_warning_message,';
2985  $sql .= ' ctf.code as type_fees_code, ctf.label as type_fees_libelle,';
2986  $sql .= ' pjt.rowid as projet_id, pjt.title as projet_title, pjt.ref as projet_ref';
2987  $sql .= ' FROM '.MAIN_DB_PREFIX.'expensereport_det as fde';
2988  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_type_fees as ctf ON fde.fk_c_type_fees=ctf.id'; // Sometimes type of expense report has been removed, so we use a left join here.
2989  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'projet as pjt ON fde.fk_projet=pjt.rowid';
2990  $sql .= ' WHERE fde.rowid = '.((int) $rowid);
2991 
2992  $result = $this->db->query($sql);
2993 
2994  if ($result) {
2995  $objp = $this->db->fetch_object($result);
2996 
2997  $this->rowid = $objp->rowid;
2998  $this->id = $objp->rowid;
2999  $this->ref = $objp->ref;
3000  $this->fk_expensereport = $objp->fk_expensereport;
3001  $this->comments = $objp->comments;
3002  $this->qty = $objp->qty;
3003  $this->date = $objp->date;
3004  $this->dates = $this->db->jdate($objp->date);
3005  $this->value_unit = $objp->value_unit;
3006  $this->fk_c_type_fees = $objp->fk_c_type_fees;
3007  $this->fk_c_exp_tax_cat = $objp->fk_c_exp_tax_cat;
3008  $this->fk_projet = $objp->fk_project; // deprecated
3009  $this->fk_project = $objp->fk_project;
3010  $this->type_fees_code = $objp->type_fees_code;
3011  $this->type_fees_libelle = $objp->type_fees_libelle;
3012  $this->projet_ref = $objp->projet_ref;
3013  $this->projet_title = $objp->projet_title;
3014 
3015  $this->vatrate = $objp->vatrate;
3016  $this->vat_src_code = $objp->vat_src_code;
3017  $this->localtax1_tx = $objp->localtax1_tx;
3018  $this->localtax2_tx = $objp->localtax2_tx;
3019  $this->localtax1_type = $objp->localtax1_type;
3020  $this->localtax2_type = $objp->localtax2_type;
3021 
3022  $this->total_ht = $objp->total_ht;
3023  $this->total_tva = $objp->total_tva;
3024  $this->total_ttc = $objp->total_ttc;
3025  $this->total_localtax1 = $objp->total_localtax1;
3026  $this->total_localtax2 = $objp->total_localtax2;
3027 
3028  $this->fk_ecm_files = $objp->fk_ecm_files;
3029 
3030  $this->rule_warning_message = $objp->rule_warning_message;
3031 
3032  $this->db->free($result);
3033 
3034  return $this->id;
3035  } else {
3036  dol_print_error($this->db);
3037  return -1;
3038  }
3039  }
3040 
3048  public function insert($notrigger = 0, $fromaddline = false)
3049  {
3050  global $user;
3051 
3052  $error = 0;
3053 
3054  dol_syslog("ExpenseReportLine::Insert", LOG_DEBUG);
3055 
3056  // Clean parameters
3057  $this->comments = trim($this->comments);
3058  if (empty($this->value_unit)) {
3059  $this->value_unit = 0;
3060  }
3061  $this->qty = (float) price2num($this->qty);
3062  $this->vatrate = price2num($this->vatrate);
3063  if (empty($this->fk_c_exp_tax_cat)) {
3064  $this->fk_c_exp_tax_cat = 0;
3065  }
3066 
3067  $this->db->begin();
3068 
3069  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'expensereport_det';
3070  $sql .= ' (fk_expensereport, fk_c_type_fees, fk_projet,';
3071  $sql .= ' tva_tx, vat_src_code,';
3072  $sql .= ' localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3073  $sql .= ' comments, qty, value_unit,';
3074  $sql .= ' total_ht, total_tva, total_ttc,';
3075  $sql .= ' total_localtax1, total_localtax2,';
3076  $sql .= ' date, rule_warning_message, fk_c_exp_tax_cat, fk_ecm_files)';
3077  $sql .= " VALUES (".$this->db->escape($this->fk_expensereport).",";
3078  $sql .= " ".((int) $this->fk_c_type_fees).",";
3079  $sql .= " ".((int) (!empty($this->fk_project) && $this->fk_project > 0) ? $this->fk_project : ((!empty($this->fk_projet) && $this->fk_projet > 0) ? $this->fk_projet : 'null')).",";
3080  $sql .= " ".((float) $this->vatrate).",";
3081  $sql .= " '".$this->db->escape(empty($this->vat_src_code) ? '' : $this->vat_src_code)."',";
3082  $sql .= " ".((float) price2num($this->localtax1_tx)).",";
3083  $sql .= " ".((float) price2num($this->localtax2_tx)).",";
3084  $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3085  $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3086  $sql .= " '".$this->db->escape($this->comments)."',";
3087  $sql .= " ".((float) $this->qty).",";
3088  $sql .= " ".((float) $this->value_unit).",";
3089  $sql .= " ".((float) price2num($this->total_ht)).",";
3090  $sql .= " ".((float) price2num($this->total_tva)).",";
3091  $sql .= " ".((float) price2num($this->total_ttc)).",";
3092  $sql .= " ".((float) price2num($this->total_localtax1)).",";
3093  $sql .= " ".((float) price2num($this->total_localtax2)).",";
3094  $sql .= " '".$this->db->idate($this->date)."',";
3095  $sql .= " ".(empty($this->rule_warning_message) ? 'null' : "'".$this->db->escape($this->rule_warning_message)."'").",";
3096  $sql .= " ".((int) $this->fk_c_exp_tax_cat).",";
3097  $sql .= " ".($this->fk_ecm_files > 0 ? ((int) $this->fk_ecm_files) : 'null');
3098  $sql .= ")";
3099 
3100  $resql = $this->db->query($sql);
3101  if ($resql) {
3102  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'expensereport_det');
3103 
3104 
3105  if (!$error && !$notrigger) {
3106  // Call triggers
3107  $result = $this->call_trigger('EXPENSE_REPORT_DET_CREATE', $user);
3108  if ($result < 0) {
3109  $error++;
3110  }
3111  // End call triggers
3112  }
3113 
3114 
3115  if (!$fromaddline) {
3116  $tmpparent = new ExpenseReport($this->db);
3117  $tmpparent->fetch($this->fk_expensereport);
3118  $result = $tmpparent->update_price(1);
3119  if ($result < 0) {
3120  $error++;
3121  $this->error = $tmpparent->error;
3122  $this->errors = $tmpparent->errors;
3123  }
3124  }
3125  } else {
3126  $error++;
3127  }
3128 
3129  if (!$error) {
3130  $this->db->commit();
3131  return $this->id;
3132  } else {
3133  $this->error = $this->db->lasterror();
3134  dol_syslog("ExpenseReportLine::insert Error ".$this->error, LOG_ERR);
3135  $this->db->rollback();
3136  return -2;
3137  }
3138  }
3139 
3148  public function getExpAmount(ExpenseReportRule $rule, $fk_user, $mode = 'day')
3149  {
3150  $amount = 0;
3151 
3152  $sql = 'SELECT SUM(d.total_ttc) as total_amount';
3153  $sql .= ' FROM '.MAIN_DB_PREFIX.'expensereport_det d';
3154  $sql .= ' INNER JOIN '.MAIN_DB_PREFIX.'expensereport e ON (d.fk_expensereport = e.rowid)';
3155  $sql .= ' WHERE e.fk_user_author = '.((int) $fk_user);
3156  if (!empty($this->id)) {
3157  $sql .= ' AND d.rowid <> '.((int) $this->id);
3158  }
3159  $sql .= ' AND d.fk_c_type_fees = '.((int) $rule->fk_c_type_fees);
3160  if ($mode == 'day' || $mode == 'EX_DAY') {
3161  $sql .= " AND d.date = '".dol_print_date($this->date, '%Y-%m-%d')."'";
3162  } elseif ($mode == 'mon' || $mode == 'EX_MON') {
3163  $sql .= " AND DATE_FORMAT(d.date, '%Y-%m') = '".dol_print_date($this->date, '%Y-%m')."'"; // @todo DATE_FORMAT is forbidden
3164  } elseif ($mode == 'year' || $mode == 'EX_YEA') {
3165  $sql .= " AND DATE_FORMAT(d.date, '%Y') = '".dol_print_date($this->date, '%Y')."'"; // @todo DATE_FORMAT is forbidden
3166  }
3167 
3168  dol_syslog('ExpenseReportLine::getExpAmount');
3169 
3170  $resql = $this->db->query($sql);
3171  if ($resql) {
3172  $num = $this->db->num_rows($resql);
3173  if ($num > 0) {
3174  $obj = $this->db->fetch_object($resql);
3175  $amount = (float) $obj->total_amount;
3176  }
3177  } else {
3178  dol_print_error($this->db);
3179  }
3180 
3181  return $amount + $this->total_ttc;
3182  }
3183 
3190  public function update(User $user)
3191  {
3192  global $langs;
3193 
3194  $error = 0;
3195 
3196  // Clean parameters
3197  $this->comments = trim($this->comments);
3198  $this->vatrate = price2num($this->vatrate);
3199  $this->value_unit = price2num($this->value_unit);
3200  if (empty($this->fk_c_exp_tax_cat)) {
3201  $this->fk_c_exp_tax_cat = 0;
3202  }
3203 
3204  $this->db->begin();
3205 
3206  // Update line in database
3207  $sql = "UPDATE ".MAIN_DB_PREFIX."expensereport_det SET";
3208  $sql .= " comments='".$this->db->escape($this->comments)."'";
3209  $sql .= ", value_unit = ".((float) $this->value_unit);
3210  $sql .= ", qty=".((float) $this->qty);
3211  $sql .= ", date='".$this->db->idate($this->date)."'";
3212  $sql .= ", total_ht=".((float) price2num($this->total_ht, 'MT'));
3213  $sql .= ", total_tva=".((float) price2num($this->total_tva, 'MT'));
3214  $sql .= ", total_ttc=".((float) price2num($this->total_ttc, 'MT'));
3215  $sql .= ", total_localtax1=".((float) price2num($this->total_localtax1, 'MT'));
3216  $sql .= ", total_localtax2=".((float) price2num($this->total_localtax2, 'MT'));
3217  $sql .= ", tva_tx=".((float) $this->vatrate);
3218  $sql .= ", vat_src_code='".$this->db->escape($this->vat_src_code)."'";
3219  $sql .= ", localtax1_tx=".((float) $this->localtax1_tx);
3220  $sql .= ", localtax2_tx=".((float) $this->localtax2_tx);
3221  $sql .= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'";
3222  $sql .= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'";
3223  $sql .= ", rule_warning_message='".$this->db->escape($this->rule_warning_message)."'";
3224  $sql .= ", fk_c_exp_tax_cat=".$this->db->escape($this->fk_c_exp_tax_cat);
3225  $sql .= ", fk_ecm_files=".($this->fk_ecm_files > 0 ? ((int) $this->fk_ecm_files) : 'null');
3226  if ($this->fk_c_type_fees) {
3227  $sql .= ", fk_c_type_fees = ".((int) $this->fk_c_type_fees);
3228  } else {
3229  $sql .= ", fk_c_type_fees=null";
3230  }
3231  if ($this->fk_project > 0) {
3232  $sql .= ", fk_projet=".((int) $this->fk_project);
3233  } else {
3234  $sql .= ", fk_projet=null";
3235  }
3236  $sql .= " WHERE rowid = ".((int) ($this->rowid ? $this->rowid : $this->id));
3237 
3238  dol_syslog("ExpenseReportLine::update");
3239 
3240  $resql = $this->db->query($sql);
3241  if ($resql) {
3242  $tmpparent = new ExpenseReport($this->db);
3243  $result = $tmpparent->fetch($this->fk_expensereport);
3244  if ($result > 0) {
3245  $result = $tmpparent->update_price(1);
3246  if ($result < 0) {
3247  $error++;
3248  $this->error = $tmpparent->error;
3249  $this->errors = $tmpparent->errors;
3250  }
3251  } else {
3252  $error++;
3253  $this->error = $tmpparent->error;
3254  $this->errors = $tmpparent->errors;
3255  }
3256  } else {
3257  $error++;
3258  dol_print_error($this->db);
3259  }
3260 
3261  if (!$error) {
3262  $this->db->commit();
3263  return 1;
3264  } else {
3265  $this->error = $this->db->lasterror();
3266  dol_syslog("ExpenseReportLine::update Error ".$this->error, LOG_ERR);
3267  $this->db->rollback();
3268  return -2;
3269  }
3270  }
3271 
3272  // ajouter ici comput_ ...
3273 }
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:624
$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.
Parent class for class inheritance lines of business objects This class is useless for the moment so ...
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
fetch_lines()
fetch_lines
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 clicable 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.
fetch($rowid)
Fetch record for expense report detailed line.
update(User $user)
Update line.
getExpAmount(ExpenseReportRule $rule, $fk_user, $mode='day')
Function to get total amount in expense reports for a same rule.
insert($notrigger=0, $fromaddline=false)
Insert a line of expense report.
__construct($db)
Constructor.
Class to manage inventories.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage Dolibarr users.
Definition: user.class.php:50
if(isModEnabled('invoice') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') && $user->hasRight('don', 'lire')) if(isModEnabled('tax') && $user->hasRight('tax', 'charges', 'lire')) if(isModEnabled('invoice') &&isModEnabled('order') && $user->hasRight("commande", "lire") &&!getDolGlobalString('WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER')) $sql
Social contributions to pay.
Definition: index.php:751
print *****$script_file(".$version.") pid c cd cd cd description as p label as s rowid
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)
Definition: files.lib.php:1620
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.
Definition: files.lib.php:1469
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.
Definition: files.lib.php:1672
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
vatrate($rate, $addpercent=false, $info_bits=0, $usestarfornpr=0, $html=0)
Return a string with VAT rate label formatted for view output Used into pdf and HTML pages.
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.
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
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 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.
div float
Unit price before taxes.
Definition: style.css.php:963
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