dolibarr  18.0.0
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-2023 Frédéric France <frederic.france@netlogic.fr>
7  * Copyright (C) 2016-2020 Ferran Marcet <fmarcet@2byte.es>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program. If not, see <https://www.gnu.org/licenses/>.
21  */
22 
28 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
29 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
30 require_once DOL_DOCUMENT_ROOT.'/expensereport/class/expensereport_ik.class.php';
31 require_once DOL_DOCUMENT_ROOT.'/expensereport/class/expensereport_rule.class.php';
32 
37 {
41  public $element = 'expensereport';
42 
46  public $table_element = 'expensereport';
47 
51  public $table_element_line = 'expensereport_det';
52 
56  public $fk_element = 'fk_expensereport';
57 
61  public $picto = 'trip';
62 
66  public $lines = array();
67 
71  public $line;
72 
73  public $date_debut;
74 
75  public $date_fin;
76 
82  public $status;
83 
90  public $fk_statut;
91 
92  public $fk_c_paiement;
93  public $modepaymentid;
94 
95  public $paid;
96 
97  public $user_author_infos;
98  public $user_validator_infos;
99 
100  public $rule_warning_message;
101 
102  // ACTIONS
103 
104  // Create
105  public $date_create;
106 
110  public $fk_user_creat;
111 
115  public $fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
116 
117  // Update
118  public $date_modif;
119  public $fk_user_modif;
120 
121  // Refus
122  public $date_refuse;
123  public $detail_refuse;
124  public $fk_user_refuse;
125 
126  // Annulation
127  public $date_cancel;
128  public $detail_cancel;
129 
133  public $fk_user_cancel;
134 
138  public $fk_user_validator;
139 
146  public $datevalid;
147 
152  public $date_valid;
153 
157  public $fk_user_valid;
158  public $user_valid_infos;
159 
160  // Approve
161  public $date_approve;
162  public $fk_user_approve; // User that has approved
163 
164  // Paiement
165  public $user_paid_infos;
166 
167  public $localtax1; // for backward compatibility (real field should be total_localtax1 defined into CommonObject)
168  public $localtax2; // for backward compatibility (real field should be total_localtax2 defined into CommonObject)
169 
170  public $statuts = array();
171  public $statuts_short = array();
172  public $statuts_logo;
173 
174  // Multicurrency
178  public $fk_multicurrency;
179 
183  public $multicurrency_code;
184  public $multicurrency_tx;
185  public $multicurrency_total_ht;
186  public $multicurrency_total_tva;
187  public $multicurrency_total_ttc;
188 
189 
193  const STATUS_DRAFT = 0;
194 
198  const STATUS_VALIDATED = 2;
199 
203  const STATUS_CANCELED = 4;
204 
208  const STATUS_APPROVED = 5;
209 
213  const STATUS_CLOSED = 6;
214 
218  const STATUS_REFUSED = 99;
219 
220  public $fields = array(
221  'rowid' =>array('type'=>'integer', 'label'=>'ID', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>10),
222  'ref' =>array('type'=>'varchar(50)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'showoncombobox'=>1, 'position'=>15),
223  'entity' =>array('type'=>'integer', 'label'=>'Entity', 'default'=>1, 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>20),
224  'ref_number_int' =>array('type'=>'integer', 'label'=>'Ref number int', 'enabled'=>1, 'visible'=>-1, 'position'=>25),
225  'ref_ext' =>array('type'=>'integer', 'label'=>'Ref ext', 'enabled'=>1, 'visible'=>-1, 'position'=>30),
226  'total_ht' =>array('type'=>'double(24,8)', 'label'=>'Total ht', 'enabled'=>1, 'visible'=>-1, 'position'=>35),
227  'total_tva' =>array('type'=>'double(24,8)', 'label'=>'Total tva', 'enabled'=>1, 'visible'=>-1, 'position'=>40),
228  'localtax1' =>array('type'=>'double(24,8)', 'label'=>'Localtax1', 'enabled'=>1, 'visible'=>-1, 'position'=>45),
229  'localtax2' =>array('type'=>'double(24,8)', 'label'=>'Localtax2', 'enabled'=>1, 'visible'=>-1, 'position'=>50),
230  'total_ttc' =>array('type'=>'double(24,8)', 'label'=>'Total ttc', 'enabled'=>1, 'visible'=>-1, 'position'=>55),
231  'date_debut' =>array('type'=>'date', 'label'=>'Date debut', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>60),
232  'date_fin' =>array('type'=>'date', 'label'=>'Date fin', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>65),
233  'date_valid' =>array('type'=>'datetime', 'label'=>'Date valid', 'enabled'=>1, 'visible'=>-1, 'position'=>75),
234  'date_approve' =>array('type'=>'datetime', 'label'=>'Date approve', 'enabled'=>1, 'visible'=>-1, 'position'=>80),
235  'date_refuse' =>array('type'=>'datetime', 'label'=>'Date refuse', 'enabled'=>1, 'visible'=>-1, 'position'=>85),
236  'date_cancel' =>array('type'=>'datetime', 'label'=>'Date cancel', 'enabled'=>1, 'visible'=>-1, 'position'=>90),
237  'fk_user_author' =>array('type'=>'integer', 'label'=>'Fk user author', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>100),
238  'fk_user_modif' =>array('type'=>'integer', 'label'=>'Fk user modif', 'enabled'=>1, 'visible'=>-1, 'position'=>105),
239  'fk_user_valid' =>array('type'=>'integer', 'label'=>'Fk user valid', 'enabled'=>1, 'visible'=>-1, 'position'=>110),
240  'fk_user_validator' =>array('type'=>'integer', 'label'=>'Fk user validator', 'enabled'=>1, 'visible'=>-1, 'position'=>115),
241  'fk_user_approve' =>array('type'=>'integer', 'label'=>'Fk user approve', 'enabled'=>1, 'visible'=>-1, 'position'=>120),
242  'fk_user_refuse' =>array('type'=>'integer', 'label'=>'Fk user refuse', 'enabled'=>1, 'visible'=>-1, 'position'=>125),
243  'fk_user_cancel' =>array('type'=>'integer', 'label'=>'Fk user cancel', 'enabled'=>1, 'visible'=>-1, 'position'=>130),
244  'fk_c_paiement' =>array('type'=>'integer', 'label'=>'Fk c paiement', 'enabled'=>1, 'visible'=>-1, 'position'=>140),
245  'paid' =>array('type'=>'integer', 'label'=>'Paid', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>145),
246  'note_public' =>array('type'=>'html', 'label'=>'Note public', 'enabled'=>1, 'visible'=>0, 'position'=>150),
247  'note_private' =>array('type'=>'html', 'label'=>'Note private', 'enabled'=>1, 'visible'=>0, 'position'=>155),
248  'detail_refuse' =>array('type'=>'varchar(255)', 'label'=>'Detail refuse', 'enabled'=>1, 'visible'=>-1, 'position'=>160),
249  'detail_cancel' =>array('type'=>'varchar(255)', 'label'=>'Detail cancel', 'enabled'=>1, 'visible'=>-1, 'position'=>165),
250  'integration_compta' =>array('type'=>'integer', 'label'=>'Integration compta', 'enabled'=>1, 'visible'=>-1, 'position'=>170),
251  'fk_bank_account' =>array('type'=>'integer', 'label'=>'Fk bank account', 'enabled'=>1, 'visible'=>-1, 'position'=>175),
252  'fk_multicurrency' =>array('type'=>'integer', 'label'=>'Fk multicurrency', 'enabled'=>1, 'visible'=>-1, 'position'=>185),
253  'multicurrency_code' =>array('type'=>'varchar(255)', 'label'=>'Multicurrency code', 'enabled'=>1, 'visible'=>-1, 'position'=>190),
254  'multicurrency_tx' =>array('type'=>'double(24,8)', 'label'=>'Multicurrency tx', 'enabled'=>1, 'visible'=>-1, 'position'=>195),
255  'multicurrency_total_ht' =>array('type'=>'double(24,8)', 'label'=>'Multicurrency total ht', 'enabled'=>1, 'visible'=>-1, 'position'=>200),
256  'multicurrency_total_tva' =>array('type'=>'double(24,8)', 'label'=>'Multicurrency total tva', 'enabled'=>1, 'visible'=>-1, 'position'=>205),
257  'multicurrency_total_ttc' =>array('type'=>'double(24,8)', 'label'=>'Multicurrency total ttc', 'enabled'=>1, 'visible'=>-1, 'position'=>210),
258  'extraparams' =>array('type'=>'varchar(255)', 'label'=>'Extraparams', 'enabled'=>1, 'visible'=>-1, 'position'=>220),
259  'date_create' =>array('type'=>'datetime', 'label'=>'Date create', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>300),
260  'tms' =>array('type'=>'timestamp', 'label'=>'Tms', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>305),
261  'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-1, 'position'=>1000),
262  'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'Model pdf', 'enabled'=>1, 'visible'=>0, 'position'=>1010),
263  'fk_statut' =>array('type'=>'integer', 'label'=>'Fk statut', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>500),
264  );
265 
271  public function __construct($db)
272  {
273  $this->db = $db;
274  $this->total_ht = 0;
275  $this->total_ttc = 0;
276  $this->total_tva = 0;
277  $this->total_localtax1 = 0;
278  $this->total_localtax2 = 0;
279  $this->localtax1 = 0; // For backward compatibility
280  $this->localtax2 = 0; // For backward compatibility
281  $this->modepaymentid = 0;
282 
283  // List of language codes for status
284  $this->statuts_short = array(0 => 'Draft', 2 => 'Validated', 4 => 'Canceled', 5 => 'Approved', 6 => 'Paid', 99 => 'Refused');
285  $this->statuts = array(0 => 'Draft', 2 => 'ValidatedWaitingApproval', 4 => 'Canceled', 5 => 'Approved', 6 => 'Paid', 99 => 'Refused');
286  $this->statuts_logo = array(0 => 'status0', 2 => 'status1', 4 => 'status6', 5 => 'status4', 6 => 'status6', 99 => 'status5');
287  }
288 
296  public function create($user, $notrigger = 0)
297  {
298  global $conf, $langs;
299 
300  $now = dol_now();
301 
302  $error = 0;
303 
304  // Check parameters
305  if (empty($this->date_debut) || empty($this->date_fin)) {
306  $this->error = $langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Date'));
307  return -1;
308  }
309 
310  $fuserid = $this->fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
311  if (empty($fuserid)) {
312  $fuserid = $user->id;
313  }
314 
315  $this->db->begin();
316 
317  $sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
318  $sql .= "ref";
319  $sql .= ",total_ht";
320  $sql .= ",total_ttc";
321  $sql .= ",total_tva";
322  $sql .= ",date_debut";
323  $sql .= ",date_fin";
324  $sql .= ",date_create";
325  $sql .= ",fk_user_creat";
326  $sql .= ",fk_user_author";
327  $sql .= ",fk_user_validator";
328  $sql .= ",fk_user_approve";
329  $sql .= ",fk_user_modif";
330  $sql .= ",fk_statut";
331  $sql .= ",fk_c_paiement";
332  $sql .= ",paid";
333  $sql .= ",note_public";
334  $sql .= ",note_private";
335  $sql .= ",entity";
336  $sql .= ") VALUES(";
337  $sql .= "'(PROV)'";
338  $sql .= ", ".price2num($this->total_ht, 'MT');
339  $sql .= ", ".price2num($this->total_ttc, 'MT');
340  $sql .= ", ".price2num($this->total_tva, 'MT');
341  $sql .= ", '".$this->db->idate($this->date_debut)."'";
342  $sql .= ", '".$this->db->idate($this->date_fin)."'";
343  $sql .= ", '".$this->db->idate($now)."'";
344  $sql .= ", ".((int) $user->id);
345  $sql .= ", ".((int) $fuserid);
346  $sql .= ", ".($this->fk_user_validator > 0 ? ((int) $this->fk_user_validator) : "null");
347  $sql .= ", ".($this->fk_user_approve > 0 ? ((int) $this->fk_user_approve) : "null");
348  $sql .= ", ".($this->fk_user_modif > 0 ? ((int) $this->fk_user_modif) : "null");
349  $sql .= ", ".($this->fk_statut > 1 ? ((int) $this->fk_statut) : 0);
350  $sql .= ", ".($this->modepaymentid ? ((int) $this->modepaymentid) : "null");
351  $sql .= ", 0";
352  $sql .= ", ".($this->note_public ? "'".$this->db->escape($this->note_public)."'" : "null");
353  $sql .= ", ".($this->note_private ? "'".$this->db->escape($this->note_private)."'" : "null");
354  $sql .= ", ".((int) $conf->entity);
355  $sql .= ")";
356 
357  $result = $this->db->query($sql);
358  if ($result) {
359  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
360  $this->ref = '(PROV'.$this->id.')';
361 
362  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element." SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
363  $resql = $this->db->query($sql);
364  if (!$resql) {
365  $this->error = $this->db->lasterror();
366  $error++;
367  }
368 
369  if (!$error) {
370  if (is_array($this->lines) && count($this->lines) > 0) {
371  foreach ($this->lines as $line) {
372  // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
373  //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
374  if (!is_object($line)) {
375  $line = (object) $line;
376  $newndfline = new ExpenseReportLine($this->db);
377  $newndfline->fk_expensereport = $line->fk_expensereport;
378  $newndfline->fk_c_type_fees = $line->fk_c_type_fees;
379  $newndfline->fk_project = $line->fk_project;
380  $newndfline->vatrate = $line->vatrate;
381  $newndfline->vat_src_code = $line->vat_src_code;
382  $newndfline->localtax1_tx = $line->localtax1_tx;
383  $newndfline->localtax2_tx = $line->localtax2_tx;
384  $newndfline->localtax1_type = $line->localtax1_type;
385  $newndfline->localtax2_type = $line->localtax2_type;
386  $newndfline->comments = $line->comments;
387  $newndfline->qty = $line->qty;
388  $newndfline->value_unit = $line->value_unit;
389  $newndfline->total_ht = $line->total_ht;
390  $newndfline->total_ttc = $line->total_ttc;
391  $newndfline->total_tva = $line->total_tva;
392  $newndfline->total_localtax1 = $line->total_localtax1;
393  $newndfline->total_localtax2 = $line->total_localtax2;
394  $newndfline->date = $line->date;
395  $newndfline->rule_warning_message = $line->rule_warning_message;
396  $newndfline->fk_c_exp_tax_cat = $line->fk_c_exp_tax_cat;
397  $newndfline->fk_ecm_files = $line->fk_ecm_files;
398  } else {
399  $newndfline = $line;
400  }
401  //$newndfline=new ExpenseReportLine($this->db);
402  $newndfline->fk_expensereport = $this->id;
403  $result = $newndfline->insert();
404  if ($result < 0) {
405  $this->error = $newndfline->error;
406  $this->errors = $newndfline->errors;
407  $error++;
408  break;
409  }
410  }
411  }
412  }
413 
414  if (!$error) {
415  $result = $this->insertExtraFields();
416  if ($result < 0) {
417  $error++;
418  }
419  }
420 
421  if (!$error) {
422  $result = $this->update_price(1);
423  if ($result > 0) {
424  if (!$notrigger) {
425  // Call trigger
426  $result = $this->call_trigger('EXPENSE_REPORT_CREATE', $user);
427 
428  if ($result < 0) {
429  $error++;
430  }
431  // End call triggers
432  }
433 
434  if (empty($error)) {
435  $this->db->commit();
436  return $this->id;
437  } else {
438  $this->db->rollback();
439  return -4;
440  }
441  } else {
442  $this->db->rollback();
443  return -3;
444  }
445  } else {
446  dol_syslog(get_class($this)."::create error ".$this->error, LOG_ERR);
447  $this->db->rollback();
448  return -2;
449  }
450  } else {
451  $this->error = $this->db->lasterror()." sql=".$sql;
452  $this->db->rollback();
453  return -1;
454  }
455  }
456 
464  public function createFromClone(User $user, $fk_user_author)
465  {
466  global $hookmanager;
467 
468  $error = 0;
469 
470  if (empty($fk_user_author)) {
471  $fk_user_author = $user->id;
472  }
473 
474  $this->db->begin();
475 
476  // get extrafields so they will be clone
477  //foreach($this->lines as $line)
478  //$line->fetch_optionals();
479 
480  // Load source object
481  $objFrom = clone $this;
482 
483  $this->id = 0;
484  $this->ref = '';
485  $this->status = 0;
486  $this->fk_statut = 0; // deprecated
487 
488  // Clear fields
489  $this->fk_user_creat = $user->id;
490  $this->fk_user_author = $fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
491  $this->fk_user_valid = '';
492  $this->date_create = '';
493  $this->date_creation = '';
494  $this->date_validation = '';
495 
496  // Remove link on lines to a joined file
497  if (is_array($this->lines) && count($this->lines) > 0) {
498  foreach ($this->lines as $key => $line) {
499  $this->lines[$key]->fk_ecm_files = 0;
500  }
501  }
502 
503  // Create clone
504  $this->context['createfromclone'] = 'createfromclone';
505  $result = $this->create($user);
506  if ($result < 0) {
507  $error++;
508  }
509 
510  if (!$error) {
511  // Hook of thirdparty module
512  if (is_object($hookmanager)) {
513  $parameters = array('objFrom'=>$objFrom);
514  $action = '';
515  $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
516  if ($reshook < 0) {
517  $this->setErrorsFromObject($hookmanager);
518  $error++;
519  }
520  }
521  }
522 
523  unset($this->context['createfromclone']);
524 
525  // End
526  if (!$error) {
527  $this->db->commit();
528  return $this->id;
529  } else {
530  $this->db->rollback();
531  return -1;
532  }
533  }
534 
535 
544  public function update($user, $notrigger = 0, $userofexpensereport = null)
545  {
546  global $langs;
547 
548  $error = 0;
549  $this->db->begin();
550 
551  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
552  $sql .= " total_ht = ".$this->total_ht;
553  $sql .= " , total_ttc = ".$this->total_ttc;
554  $sql .= " , total_tva = ".$this->total_tva;
555  $sql .= " , date_debut = '".$this->db->idate($this->date_debut)."'";
556  $sql .= " , date_fin = '".$this->db->idate($this->date_fin)."'";
557  if ($userofexpensereport && is_object($userofexpensereport)) {
558  $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.
559  }
560  $sql .= " , fk_user_validator = ".($this->fk_user_validator > 0 ? $this->fk_user_validator : "null");
561  $sql .= " , fk_user_valid = ".($this->fk_user_valid > 0 ? $this->fk_user_valid : "null");
562  $sql .= " , fk_user_approve = ".($this->fk_user_approve > 0 ? $this->fk_user_approve : "null");
563  $sql .= " , fk_user_modif = ".$user->id;
564  $sql .= " , fk_statut = ".($this->fk_statut >= 0 ? $this->fk_statut : '0');
565  $sql .= " , fk_c_paiement = ".($this->fk_c_paiement > 0 ? $this->fk_c_paiement : "null");
566  $sql .= " , note_public = ".(!empty($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "''");
567  $sql .= " , note_private = ".(!empty($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "''");
568  $sql .= " , detail_refuse = ".(!empty($this->detail_refuse) ? "'".$this->db->escape($this->detail_refuse)."'" : "''");
569  $sql .= " WHERE rowid = ".((int) $this->id);
570 
571  dol_syslog(get_class($this)."::update", LOG_DEBUG);
572  $result = $this->db->query($sql);
573  if ($result) {
574  if (!$notrigger) {
575  // Call trigger
576  $result = $this->call_trigger('EXPENSE_REPORT_MODIFY', $user);
577 
578  if ($result < 0) {
579  $error++;
580  }
581  // End call triggers
582  }
583 
584  if (empty($error)) {
585  $this->db->commit();
586  return 1;
587  } else {
588  $this->db->rollback();
589  $this->error = $this->db->error();
590  return -2;
591  }
592  } else {
593  $this->db->rollback();
594  $this->error = $this->db->error();
595  return -1;
596  }
597  }
598 
606  public function fetch($id, $ref = '')
607  {
608  global $conf;
609 
610  $sql = "SELECT d.rowid, d.entity, d.ref, d.note_public, d.note_private,"; // DEFAULT
611  $sql .= " d.detail_refuse, d.detail_cancel, d.fk_user_refuse, d.fk_user_cancel,"; // ACTIONS
612  $sql .= " d.date_refuse, d.date_cancel,"; // ACTIONS
613  $sql .= " d.total_ht, d.total_ttc, d.total_tva,";
614  $sql .= " d.localtax1 as total_localtax1, d.localtax2 as total_localtax2,";
615  $sql .= " d.date_debut, d.date_fin, d.date_create, d.tms as date_modif, d.date_valid, d.date_approve,"; // DATES (datetime)
616  $sql .= " d.fk_user_creat, d.fk_user_author, d.fk_user_modif, d.fk_user_validator,";
617  $sql .= " d.fk_user_valid, d.fk_user_approve,";
618  $sql .= " d.fk_statut as status, d.fk_c_paiement, d.paid";
619  $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as d";
620  if ($ref) {
621  $sql .= " WHERE d.ref = '".$this->db->escape($ref)."'";
622  } else {
623  $sql .= " WHERE d.rowid = ".((int) $id);
624  }
625  //$sql.= $restrict;
626 
627  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
628  $resql = $this->db->query($sql);
629  if ($resql) {
630  $obj = $this->db->fetch_object($resql);
631  if ($obj) {
632  $this->id = $obj->rowid;
633  $this->ref = $obj->ref;
634 
635  $this->entity = $obj->entity;
636 
637  $this->total_ht = $obj->total_ht;
638  $this->total_tva = $obj->total_tva;
639  $this->total_ttc = $obj->total_ttc;
640  $this->localtax1 = $obj->total_localtax1; // For backward compatibility
641  $this->localtax2 = $obj->total_localtax2; // For backward compatibility
642  $this->total_localtax1 = $obj->total_localtax1;
643  $this->total_localtax2 = $obj->total_localtax2;
644 
645  $this->note_public = $obj->note_public;
646  $this->note_private = $obj->note_private;
647  $this->detail_refuse = $obj->detail_refuse;
648  $this->detail_cancel = $obj->detail_cancel;
649 
650  $this->date_debut = $this->db->jdate($obj->date_debut);
651  $this->date_fin = $this->db->jdate($obj->date_fin);
652  $this->date_valid = $this->db->jdate($obj->date_valid);
653  $this->date_approve = $this->db->jdate($obj->date_approve);
654  $this->date_create = $this->db->jdate($obj->date_create);
655  $this->date_modif = $this->db->jdate($obj->date_modif);
656  $this->date_refuse = $this->db->jdate($obj->date_refuse);
657  $this->date_cancel = $this->db->jdate($obj->date_cancel);
658 
659  $this->fk_user_creat = $obj->fk_user_creat;
660  $this->fk_user_author = $obj->fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
661  $this->fk_user_modif = $obj->fk_user_modif;
662  $this->fk_user_validator = $obj->fk_user_validator;
663  $this->fk_user_valid = $obj->fk_user_valid;
664  $this->fk_user_refuse = $obj->fk_user_refuse;
665  $this->fk_user_cancel = $obj->fk_user_cancel;
666  $this->fk_user_approve = $obj->fk_user_approve;
667 
668  $user_author = new User($this->db);
669  if ($this->fk_user_author > 0) {
670  $user_author->fetch($this->fk_user_author);
671  }
672 
673  $this->user_author_infos = dolGetFirstLastname($user_author->firstname, $user_author->lastname);
674 
675  $user_approver = new User($this->db);
676  if ($this->fk_user_approve > 0) {
677  $user_approver->fetch($this->fk_user_approve);
678  } elseif ($this->fk_user_validator > 0) {
679  $user_approver->fetch($this->fk_user_validator); // For backward compatibility
680  }
681  $this->user_validator_infos = dolGetFirstLastname($user_approver->firstname, $user_approver->lastname);
682 
683  $this->fk_statut = $obj->status; // deprecated
684  $this->status = $obj->status;
685  $this->fk_c_paiement = $obj->fk_c_paiement;
686  $this->paid = $obj->paid;
687 
688  if ($this->status == self::STATUS_APPROVED || $this->status == self::STATUS_CLOSED) {
689  $user_valid = new User($this->db);
690  if ($this->fk_user_valid > 0) {
691  $user_valid->fetch($this->fk_user_valid);
692  }
693  $this->user_valid_infos = dolGetFirstLastname($user_valid->firstname, $user_valid->lastname);
694  }
695 
696  $this->fetch_optionals();
697 
698  $result = $this->fetch_lines();
699 
700  return $result;
701  } else {
702  return 0;
703  }
704  } else {
705  $this->error = $this->db->lasterror();
706  return -1;
707  }
708  }
709 
710  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
721  public function set_paid($id, $fuser, $notrigger = 0)
722  {
723  // phpcs:enable
724  dol_syslog(get_class($this)."::set_paid is deprecated, use setPaid instead", LOG_NOTICE);
725  return $this->setPaid($id, $fuser, $notrigger);
726  }
727 
736  public function setPaid($id, $fuser, $notrigger = 0)
737  {
738  $error = 0;
739  $this->db->begin();
740 
741  $sql = "UPDATE ".MAIN_DB_PREFIX."expensereport";
742  $sql .= " SET fk_statut = ".self::STATUS_CLOSED.", paid=1";
743  $sql .= " WHERE rowid = ".((int) $id)." AND fk_statut = ".self::STATUS_APPROVED;
744 
745  dol_syslog(get_class($this)."::set_paid", LOG_DEBUG);
746  $resql = $this->db->query($sql);
747  if ($resql) {
748  if ($this->db->affected_rows($resql)) {
749  if (!$notrigger) {
750  // Call trigger
751  $result = $this->call_trigger('EXPENSE_REPORT_PAID', $fuser);
752 
753  if ($result < 0) {
754  $error++;
755  }
756  // End call triggers
757  }
758 
759  if (empty($error)) {
760  $this->db->commit();
761  return 1;
762  } else {
763  $this->db->rollback();
764  $this->error = $this->db->error();
765  return -2;
766  }
767  } else {
768  $this->db->commit();
769  return 0;
770  }
771  } else {
772  $this->db->rollback();
773  dol_print_error($this->db);
774  return -1;
775  }
776  }
777 
784  public function getLibStatut($mode = 0)
785  {
786  return $this->LibStatut($this->status, $mode);
787  }
788 
789  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
797  public function LibStatut($status, $mode = 0)
798  {
799  // phpcs:enable
800  global $langs;
801 
802  $labelStatus = $langs->transnoentitiesnoconv($this->statuts[$status]);
803  $labelStatusShort = $langs->transnoentitiesnoconv($this->statuts_short[$status]);
804 
805  $statusType = $this->statuts_logo[$status];
806 
807  return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode);
808  }
809 
810 
817  public function info($id)
818  {
819  global $conf;
820 
821  $sql = "SELECT f.rowid,";
822  $sql .= " f.date_create as datec,";
823  $sql .= " f.tms as date_modification,";
824  $sql .= " f.date_valid as datev,";
825  $sql .= " f.date_approve as datea,";
826  $sql .= " f.fk_user_creat as fk_user_creation,";
827  $sql .= " f.fk_user_author as fk_user_author,";
828  $sql .= " f.fk_user_modif as fk_user_modification,";
829  $sql .= " f.fk_user_valid,";
830  $sql .= " f.fk_user_approve";
831  $sql .= " FROM ".MAIN_DB_PREFIX."expensereport as f";
832  $sql .= " WHERE f.rowid = ".((int) $id);
833  $sql .= " AND f.entity = ".$conf->entity;
834 
835  $resql = $this->db->query($sql);
836  if ($resql) {
837  if ($this->db->num_rows($resql)) {
838  $obj = $this->db->fetch_object($resql);
839 
840  $this->id = $obj->rowid;
841 
842  $this->date_creation = $this->db->jdate($obj->datec);
843  $this->date_modification = $this->db->jdate($obj->date_modification);
844  $this->date_validation = $this->db->jdate($obj->datev);
845  $this->date_approbation = $this->db->jdate($obj->datea);
846 
847  $cuser = new User($this->db);
848  $cuser->fetch($obj->fk_user_author);
849  $this->user_creation = $cuser;
850 
851  if ($obj->fk_user_creation) {
852  $cuser = new User($this->db);
853  $cuser->fetch($obj->fk_user_creation);
854  $this->user_creation = $cuser;
855  }
856  if ($obj->fk_user_valid) {
857  $vuser = new User($this->db);
858  $vuser->fetch($obj->fk_user_valid);
859  $this->user_validation = $vuser;
860  }
861  if ($obj->fk_user_modification) {
862  $muser = new User($this->db);
863  $muser->fetch($obj->fk_user_modification);
864  $this->user_modification = $muser;
865  }
866  if ($obj->fk_user_approve) {
867  $auser = new User($this->db);
868  $auser->fetch($obj->fk_user_approve);
869  $this->user_approve = $auser;
870  }
871  }
872  $this->db->free($resql);
873  } else {
874  dol_print_error($this->db);
875  }
876  }
877 
878 
879 
887  public function initAsSpecimen()
888  {
889  global $user, $langs, $conf;
890 
891  $now = dol_now();
892 
893  // Initialise parametres
894  $this->id = 0;
895  $this->ref = 'SPECIMEN';
896  $this->specimen = 1;
897  $this->entity = 1;
898  $this->date_create = $now;
899  $this->date_debut = $now;
900  $this->date_fin = $now;
901  $this->date_valid = $now;
902  $this->date_approve = $now;
903 
904  $type_fees_id = 2; // TF_TRIP
905 
906  $this->status = 5;
907  $this->fk_statut = 5;
908 
909  $this->fk_user_author = $user->id;
910  $this->fk_user_validator = $user->id;
911  $this->fk_user_valid = $user->id;
912  $this->fk_user_approve = $user->id;
913 
914  $this->note_private = 'Private note';
915  $this->note_public = 'SPECIMEN';
916  $nbp = 5;
917  $xnbp = 0;
918  while ($xnbp < $nbp) {
919  $line = new ExpenseReportLine($this->db);
920  $line->comments = $langs->trans("Comment")." ".$xnbp;
921  $line->date = ($now - 3600 * (1 + $xnbp));
922  $line->total_ht = 100;
923  $line->total_tva = 20;
924  $line->total_ttc = 120;
925  $line->qty = 1;
926  $line->vatrate = 20;
927  $line->value_unit = 120;
928  $line->fk_expensereport = 0;
929  $line->type_fees_code = 'TRA';
930  $line->fk_c_type_fees = $type_fees_id;
931 
932  $line->projet_ref = 'ABC';
933 
934  $this->lines[$xnbp] = $line;
935  $xnbp++;
936 
937  $this->total_ht += $line->total_ht;
938  $this->total_tva += $line->total_tva;
939  $this->total_ttc += $line->total_ttc;
940  }
941  }
942 
943  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
951  public function fetch_line_by_project($projectid, $user = '')
952  {
953  // phpcs:enable
954  global $conf, $db, $langs;
955 
956  $langs->load('trips');
957 
958  if ($user->rights->expensereport->lire) {
959  $sql = "SELECT de.fk_expensereport, de.date, de.comments, de.total_ht, de.total_ttc";
960  $sql .= " FROM ".MAIN_DB_PREFIX."expensereport_det as de";
961  $sql .= " WHERE de.fk_projet = ".((int) $projectid);
962 
963  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
964  $result = $this->db->query($sql);
965  if ($result) {
966  $num = $this->db->num_rows($result);
967  $i = 0;
968  $total_HT = 0;
969  $total_TTC = 0;
970 
971  while ($i < $num) {
972  $objp = $this->db->fetch_object($result);
973 
974  $sql2 = "SELECT d.rowid, d.fk_user_author, d.ref, d.fk_statut as status";
975  $sql2 .= " FROM ".MAIN_DB_PREFIX."expensereport as d";
976  $sql2 .= " WHERE d.rowid = ".((int) $objp->fk_expensereport);
977 
978  $result2 = $this->db->query($sql2);
979  $obj = $this->db->fetch_object($result2);
980 
981  $objp->fk_user_author = $obj->fk_user_author;
982  $objp->ref = $obj->ref;
983  $objp->fk_c_expensereport_status = $obj->status;
984  $objp->rowid = $obj->rowid;
985 
986  $total_HT = $total_HT + $objp->total_ht;
987  $total_TTC = $total_TTC + $objp->total_ttc;
988  $author = new User($this->db);
989  $author->fetch($objp->fk_user_author);
990 
991  print '<tr>';
992  print '<td><a href="'.DOL_URL_ROOT.'/expensereport/card.php?id='.$objp->rowid.'">'.$objp->ref_num.'</a></td>';
993  print '<td class="center">'.dol_print_date($objp->date, 'day').'</td>';
994  print '<td>'.$author->getNomUrl(1).'</td>';
995  print '<td>'.$objp->comments.'</td>';
996  print '<td class="right">'.price($objp->total_ht).'</td>';
997  print '<td class="right">'.price($objp->total_ttc).'</td>';
998  print '<td class="right">';
999 
1000  switch ($objp->fk_c_expensereport_status) {
1001  case 4:
1002  print img_picto($langs->trans('StatusOrderCanceled'), 'statut5');
1003  break;
1004  case 1:
1005  print $langs->trans('Draft').' '.img_picto($langs->trans('Draft'), 'statut0');
1006  break;
1007  case 2:
1008  print $langs->trans('TripForValid').' '.img_picto($langs->trans('TripForValid'), 'statut3');
1009  break;
1010  case 5:
1011  print $langs->trans('TripForPaid').' '.img_picto($langs->trans('TripForPaid'), 'statut3');
1012  break;
1013  case 6:
1014  print $langs->trans('TripPaid').' '.img_picto($langs->trans('TripPaid'), 'statut4');
1015  break;
1016  }
1017  /*
1018  if ($status==4) return img_picto($langs->trans('StatusOrderCanceled'),'statut5');
1019  if ($status==1) return img_picto($langs->trans('StatusOrderDraft'),'statut0');
1020  if ($status==2) return img_picto($langs->trans('StatusOrderValidated'),'statut1');
1021  if ($status==2) return img_picto($langs->trans('StatusOrderOnProcess'),'statut3');
1022  if ($status==5) return img_picto($langs->trans('StatusOrderToBill'),'statut4');
1023  if ($status==6) return img_picto($langs->trans('StatusOrderOnProcess'),'statut6');
1024  */
1025  print '</td>';
1026  print '</tr>';
1027 
1028  $i++;
1029  }
1030 
1031  print '<tr class="liste_total"><td colspan="4">'.$langs->trans("Number").': '.$i.'</td>';
1032  print '<td class="right" width="100">'.$langs->trans("TotalHT").' : '.price($total_HT).'</td>';
1033  print '<td class="right" width="100">'.$langs->trans("TotalTTC").' : '.price($total_TTC).'</td>';
1034  print '<td>&nbsp;</td>';
1035  print '</tr>';
1036  } else {
1037  $this->error = $this->db->lasterror();
1038  return -1;
1039  }
1040  }
1041 
1042  return 0;
1043  }
1044 
1045  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1051  public function fetch_lines()
1052  {
1053  // phpcs:enable
1054  global $conf;
1055 
1056  $this->lines = array();
1057 
1058  $sql = ' SELECT de.rowid, de.comments, de.qty, de.value_unit, de.date, de.rang,';
1059  $sql .= " de.".$this->fk_element.", de.fk_c_type_fees, de.fk_c_exp_tax_cat, de.fk_projet as fk_project,";
1060  $sql .= ' de.tva_tx, de.vat_src_code,';
1061  $sql .= ' de.localtax1_tx, de.localtax2_tx, de.localtax1_type, de.localtax2_type,';
1062  $sql .= ' de.fk_ecm_files,';
1063  $sql .= ' de.total_ht, de.total_tva, de.total_ttc,';
1064  $sql .= ' de.total_localtax1, de.total_localtax2, de.rule_warning_message,';
1065  $sql .= ' ctf.code as code_type_fees, ctf.label as libelle_type_fees, ctf.accountancy_code as accountancy_code_type_fees,';
1066  $sql .= ' p.ref as ref_projet, p.title as title_projet';
1067  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line.' as de';
1068  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_type_fees as ctf ON de.fk_c_type_fees = ctf.id';
1069  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'projet as p ON de.fk_projet = p.rowid';
1070  $sql .= " WHERE de.".$this->fk_element." = ".((int) $this->id);
1071  if (!empty($conf->global->EXPENSEREPORT_LINES_SORTED_BY_ROWID)) {
1072  $sql .= ' ORDER BY de.rang ASC, de.rowid ASC';
1073  } else {
1074  $sql .= ' ORDER BY de.rang ASC, de.date ASC';
1075  }
1076 
1077  $resql = $this->db->query($sql);
1078  if ($resql) {
1079  $num = $this->db->num_rows($resql);
1080  $i = 0;
1081  while ($i < $num) {
1082  $objp = $this->db->fetch_object($resql);
1083 
1084  $deplig = new ExpenseReportLine($this->db);
1085 
1086  $deplig->rowid = $objp->rowid;
1087  $deplig->id = $objp->rowid;
1088  $deplig->comments = $objp->comments;
1089  $deplig->qty = $objp->qty;
1090  $deplig->value_unit = $objp->value_unit;
1091  $deplig->date = $objp->date;
1092  $deplig->dates = $this->db->jdate($objp->date);
1093 
1094  $deplig->fk_expensereport = $objp->fk_expensereport;
1095  $deplig->fk_c_type_fees = $objp->fk_c_type_fees;
1096  $deplig->fk_c_exp_tax_cat = $objp->fk_c_exp_tax_cat;
1097  $deplig->fk_projet = $objp->fk_project; // deprecated
1098  $deplig->fk_project = $objp->fk_project;
1099  $deplig->fk_ecm_files = $objp->fk_ecm_files;
1100 
1101  $deplig->total_ht = $objp->total_ht;
1102  $deplig->total_tva = $objp->total_tva;
1103  $deplig->total_ttc = $objp->total_ttc;
1104  $deplig->total_localtax1 = $objp->total_localtax1;
1105  $deplig->total_localtax2 = $objp->total_localtax2;
1106 
1107  $deplig->type_fees_code = empty($objp->code_type_fees) ? 'TF_OTHER' : $objp->code_type_fees;
1108  $deplig->type_fees_libelle = $objp->libelle_type_fees;
1109  $deplig->type_fees_accountancy_code = $objp->accountancy_code_type_fees;
1110 
1111  $deplig->tva_tx = $objp->tva_tx;
1112  $deplig->vatrate = $objp->tva_tx;
1113  $deplig->vat_src_code = $objp->vat_src_code;
1114  $deplig->localtax1_tx = $objp->localtax1_tx;
1115  $deplig->localtax2_tx = $objp->localtax2_tx;
1116  $deplig->localtax1_type = $objp->localtax1_type;
1117  $deplig->localtax2_type = $objp->localtax2_type;
1118 
1119  $deplig->projet_ref = $objp->ref_projet;
1120  $deplig->projet_title = $objp->title_projet;
1121 
1122  $deplig->rule_warning_message = $objp->rule_warning_message;
1123 
1124  $deplig->rang = $objp->rang;
1125 
1126  $this->lines[$i] = $deplig;
1127 
1128  $i++;
1129  }
1130  $this->db->free($resql);
1131  return 1;
1132  } else {
1133  $this->error = $this->db->lasterror();
1134  dol_syslog(get_class($this)."::fetch_lines: Error ".$this->error, LOG_ERR);
1135  return -3;
1136  }
1137  }
1138 
1139 
1147  public function delete(User $user = null, $notrigger = false)
1148  {
1149  global $conf;
1150  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1151 
1152  $error = 0;
1153 
1154  $this->db->begin();
1155 
1156  if (!$notrigger) {
1157  // Call trigger
1158  $result = $this->call_trigger('EXPENSE_REPORT_DELETE', $user);
1159  if ($result < 0) {
1160  $error++;
1161  }
1162  // End call triggers
1163  }
1164 
1165  // Delete extrafields of lines and lines
1166  if (!$error && !empty($this->table_element_line)) {
1167  $tabletodelete = $this->table_element_line;
1168  //$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).")";
1169  $sql = "DELETE FROM ".MAIN_DB_PREFIX.$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id);
1170  if (!$this->db->query($sql)) {
1171  $error++;
1172  $this->error = $this->db->lasterror();
1173  $this->errors[] = $this->error;
1174  dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
1175  }
1176  }
1177 
1178  if (!$error) {
1179  // Delete linked object
1180  $res = $this->deleteObjectLinked();
1181  if ($res < 0) {
1182  $error++;
1183  }
1184  }
1185 
1186  if (!$error) {
1187  // Delete linked contacts
1188  $res = $this->delete_linked_contact();
1189  if ($res < 0) {
1190  $error++;
1191  }
1192  }
1193 
1194  // Removed extrafields of object
1195  if (!$error) {
1196  $result = $this->deleteExtraFields();
1197  if ($result < 0) {
1198  $error++;
1199  dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
1200  }
1201  }
1202 
1203  // Delete main record
1204  if (!$error) {
1205  $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element." WHERE rowid = ".((int) $this->id);
1206  $res = $this->db->query($sql);
1207  if (!$res) {
1208  $error++;
1209  $this->error = $this->db->lasterror();
1210  $this->errors[] = $this->error;
1211  dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
1212  }
1213  }
1214 
1215  // Delete record into ECM index and physically
1216  if (!$error) {
1217  $res = $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
1218  if (!$res) {
1219  $error++;
1220  }
1221  }
1222 
1223  if (!$error) {
1224  // We remove directory
1225  $ref = dol_sanitizeFileName($this->ref);
1226  if ($conf->expensereport->multidir_output[$this->entity] && !empty($this->ref)) {
1227  $dir = $conf->expensereport->multidir_output[$this->entity]."/".$ref;
1228  $file = $dir."/".$ref.".pdf";
1229  if (file_exists($file)) {
1230  dol_delete_preview($this);
1231 
1232  if (!dol_delete_file($file, 0, 0, 0, $this)) {
1233  $this->error = 'ErrorFailToDeleteFile';
1234  $this->errors[] = $this->error;
1235  $this->db->rollback();
1236  return 0;
1237  }
1238  }
1239  if (file_exists($dir)) {
1240  $res = @dol_delete_dir_recursive($dir);
1241  if (!$res) {
1242  $this->error = 'ErrorFailToDeleteDir';
1243  $this->errors[] = $this->error;
1244  $this->db->rollback();
1245  return 0;
1246  }
1247  }
1248  }
1249  }
1250 
1251  if (!$error) {
1252  dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
1253  $this->db->commit();
1254  return 1;
1255  } else {
1256  $this->db->rollback();
1257  return -1;
1258  }
1259  }
1260 
1268  public function setValidate($fuser, $notrigger = 0)
1269  {
1270  global $conf, $langs, $user;
1271 
1272  $error = 0;
1273  $now = dol_now();
1274 
1275  // Protection
1276  if ($this->status == self::STATUS_VALIDATED) {
1277  dol_syslog(get_class($this)."::valid action abandonned: already validated", LOG_WARNING);
1278  return 0;
1279  }
1280 
1281  $this->date_valid = $now; // Required for the getNextNum later.
1282 
1283  // Define new ref
1284  if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
1285  $num = $this->getNextNumRef();
1286  } else {
1287  $num = $this->ref;
1288  }
1289  if (empty($num) || $num < 0) {
1290  return -1;
1291  }
1292 
1293  $this->newref = dol_sanitizeFileName($num);
1294 
1295  $this->db->begin();
1296 
1297  // Validate
1298  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
1299  $sql .= " SET ref = '".$this->db->escape($num)."',";
1300  $sql .= " fk_statut = ".self::STATUS_VALIDATED.",";
1301  $sql .= " date_valid='".$this->db->idate($this->date_valid)."',";
1302  $sql .= " fk_user_valid = ".$user->id;
1303  $sql .= " WHERE rowid = ".((int) $this->id);
1304 
1305  $resql = $this->db->query($sql);
1306  if ($resql) {
1307  if (!$error && !$notrigger) {
1308  // Call trigger
1309  $result = $this->call_trigger('EXPENSE_REPORT_VALIDATE', $fuser);
1310  if ($result < 0) {
1311  $error++;
1312  }
1313  // End call triggers
1314  }
1315 
1316  if (!$error) {
1317  $this->oldref = $this->ref;
1318 
1319  // Rename directory if dir was a temporary ref
1320  if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1321  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1322 
1323  // Now we rename also files into index
1324  $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)."'";
1325  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'expensereport/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1326  $resql = $this->db->query($sql);
1327  if (!$resql) {
1328  $error++; $this->error = $this->db->lasterror();
1329  }
1330 
1331  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1332  $oldref = dol_sanitizeFileName($this->ref);
1333  $newref = dol_sanitizeFileName($num);
1334  $dirsource = $conf->expensereport->dir_output.'/'.$oldref;
1335  $dirdest = $conf->expensereport->dir_output.'/'.$newref;
1336  if (!$error && file_exists($dirsource)) {
1337  dol_syslog(get_class($this)."::setValidate() rename dir ".$dirsource." into ".$dirdest);
1338 
1339  if (@rename($dirsource, $dirdest)) {
1340  dol_syslog("Rename ok");
1341  // Rename docs starting with $oldref with $newref
1342  $listoffiles = dol_dir_list($conf->expensereport->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1343  foreach ($listoffiles as $fileentry) {
1344  $dirsource = $fileentry['name'];
1345  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1346  $dirsource = $fileentry['path'].'/'.$dirsource;
1347  $dirdest = $fileentry['path'].'/'.$dirdest;
1348  @rename($dirsource, $dirdest);
1349  }
1350  }
1351  }
1352  }
1353  }
1354 
1355  // Set new ref and current status
1356  if (!$error) {
1357  $this->ref = $num;
1358  $this->status = self::STATUS_VALIDATED;
1359  }
1360 
1361  if (empty($error)) {
1362  $this->db->commit();
1363  return 1;
1364  } else {
1365  $this->db->rollback();
1366  $this->error = $this->db->error();
1367  return -2;
1368  }
1369  } else {
1370  $this->db->rollback();
1371  $this->error = $this->db->lasterror();
1372  return -1;
1373  }
1374  }
1375 
1376  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1383  public function set_save_from_refuse($fuser)
1384  {
1385  // phpcs:enable
1386  // Sélection de la date de début de la NDF
1387  $sql = 'SELECT date_debut';
1388  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element;
1389  $sql .= " WHERE rowid = ".((int) $this->id);
1390 
1391  $result = $this->db->query($sql);
1392 
1393  $objp = $this->db->fetch_object($result);
1394 
1395  $this->date_debut = $this->db->jdate($objp->date_debut);
1396 
1397  if ($this->status != self::STATUS_VALIDATED) {
1398  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1399  $sql .= " SET fk_statut = ".self::STATUS_VALIDATED;
1400  $sql .= " WHERE rowid = ".((int) $this->id);
1401 
1402  dol_syslog(get_class($this)."::set_save_from_refuse", LOG_DEBUG);
1403 
1404  if ($this->db->query($sql)) {
1405  return 1;
1406  } else {
1407  $this->error = $this->db->lasterror();
1408  return -1;
1409  }
1410  } else {
1411  dol_syslog(get_class($this)."::set_save_from_refuse expensereport already with save status", LOG_WARNING);
1412  }
1413 
1414  return 0;
1415  }
1416 
1424  public function setApproved($fuser, $notrigger = 0)
1425  {
1426  $now = dol_now();
1427  $error = 0;
1428 
1429  // date approval
1430  $this->date_approve = $now;
1431  if ($this->status != self::STATUS_APPROVED) {
1432  $this->db->begin();
1433 
1434  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1435  $sql .= " SET ref = '".$this->db->escape($this->ref)."', fk_statut = ".self::STATUS_APPROVED.", fk_user_approve = ".((int) $fuser->id).",";
1436  $sql .= " date_approve='".$this->db->idate($this->date_approve)."'";
1437  $sql .= " WHERE rowid = ".((int) $this->id);
1438  if ($this->db->query($sql)) {
1439  if (!$notrigger) {
1440  // Call trigger
1441  $result = $this->call_trigger('EXPENSE_REPORT_APPROVE', $fuser);
1442 
1443  if ($result < 0) {
1444  $error++;
1445  }
1446  // End call triggers
1447  }
1448 
1449  if (empty($error)) {
1450  $this->db->commit();
1451  return 1;
1452  } else {
1453  $this->db->rollback();
1454  $this->error = $this->db->error();
1455  return -2;
1456  }
1457  } else {
1458  $this->db->rollback();
1459  $this->error = $this->db->lasterror();
1460  return -1;
1461  }
1462  } else {
1463  dol_syslog(get_class($this)."::setApproved expensereport already with approve status", LOG_WARNING);
1464  }
1465 
1466  return 0;
1467  }
1468 
1477  public function setDeny($fuser, $details, $notrigger = 0)
1478  {
1479  $now = dol_now();
1480  $error = 0;
1481 
1482  // date de refus
1483  if ($this->status != self::STATUS_REFUSED) {
1484  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1485  $sql .= " SET ref = '".$this->db->escape($this->ref)."', fk_statut = ".self::STATUS_REFUSED.", fk_user_refuse = ".((int) $fuser->id).",";
1486  $sql .= " date_refuse='".$this->db->idate($now)."',";
1487  $sql .= " detail_refuse='".$this->db->escape($details)."',";
1488  $sql .= " fk_user_approve = NULL";
1489  $sql .= " WHERE rowid = ".((int) $this->id);
1490  if ($this->db->query($sql)) {
1491  $this->fk_statut = 99; // deprecated
1492  $this->status = 99;
1493  $this->fk_user_refuse = $fuser->id;
1494  $this->detail_refuse = $details;
1495  $this->date_refuse = $now;
1496 
1497  if (!$notrigger) {
1498  // Call trigger
1499  $result = $this->call_trigger('EXPENSE_REPORT_DENY', $fuser);
1500 
1501  if ($result < 0) {
1502  $error++;
1503  }
1504  // End call triggers
1505  }
1506 
1507  if (empty($error)) {
1508  $this->db->commit();
1509  return 1;
1510  } else {
1511  $this->db->rollback();
1512  $this->error = $this->db->error();
1513  return -2;
1514  }
1515  } else {
1516  $this->db->rollback();
1517  $this->error = $this->db->lasterror();
1518  return -1;
1519  }
1520  } else {
1521  dol_syslog(get_class($this)."::setDeny expensereport already with refuse status", LOG_WARNING);
1522  }
1523 
1524  return 0;
1525  }
1526 
1527  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1537  public function set_unpaid($fuser, $notrigger = 0)
1538  {
1539  // phpcs:enable
1540  dol_syslog(get_class($this)."::set_unpaid is deprecated, use setUnpaid instead", LOG_NOTICE);
1541  return $this->setUnpaid($fuser, $notrigger);
1542  }
1543 
1551  public function setUnpaid($fuser, $notrigger = 0)
1552  {
1553  $error = 0;
1554 
1555  if ($this->paid) {
1556  $this->db->begin();
1557 
1558  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1559  $sql .= " SET paid = 0, fk_statut = ".self::STATUS_APPROVED;
1560  $sql .= " WHERE rowid = ".((int) $this->id);
1561 
1562  dol_syslog(get_class($this)."::set_unpaid", LOG_DEBUG);
1563 
1564  if ($this->db->query($sql)) {
1565  if (!$notrigger) {
1566  // Call trigger
1567  $result = $this->call_trigger('EXPENSE_REPORT_UNPAID', $fuser);
1568 
1569  if ($result < 0) {
1570  $error++;
1571  }
1572  // End call triggers
1573  }
1574 
1575  if (empty($error)) {
1576  $this->db->commit();
1577  return 1;
1578  } else {
1579  $this->db->rollback();
1580  $this->error = $this->db->error();
1581  return -2;
1582  }
1583  } else {
1584  $this->db->rollback();
1585  $this->error = $this->db->error();
1586  return -1;
1587  }
1588  } else {
1589  dol_syslog(get_class($this)."::set_unpaid expensereport already with unpaid status", LOG_WARNING);
1590  }
1591 
1592  return 0;
1593  }
1594 
1595  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1604  public function set_cancel($fuser, $detail, $notrigger = 0)
1605  {
1606  // phpcs:enable
1607  $error = 0;
1608  $this->date_cancel = $this->db->idate(dol_now());
1609  if ($this->status != self::STATUS_CANCELED) {
1610  $this->db->begin();
1611 
1612  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1613  $sql .= " SET fk_statut = ".self::STATUS_CANCELED.", fk_user_cancel = ".((int) $fuser->id);
1614  $sql .= ", date_cancel='".$this->db->idate($this->date_cancel)."'";
1615  $sql .= ", detail_cancel='".$this->db->escape($detail)."'";
1616  $sql .= " WHERE rowid = ".((int) $this->id);
1617 
1618  dol_syslog(get_class($this)."::set_cancel", LOG_DEBUG);
1619 
1620  if ($this->db->query($sql)) {
1621  if (!$notrigger) {
1622  // Call trigger
1623  $result = $this->call_trigger('EXPENSE_REPORT_CANCEL', $fuser);
1624 
1625  if ($result < 0) {
1626  $error++;
1627  }
1628  // End call triggers
1629  }
1630 
1631  if (empty($error)) {
1632  $this->db->commit();
1633  return 1;
1634  } else {
1635  $this->db->rollback();
1636  $this->error = $this->db->error();
1637  return -2;
1638  }
1639  } else {
1640  $this->db->rollback();
1641  $this->error = $this->db->error();
1642  return -1;
1643  }
1644  } else {
1645  dol_syslog(get_class($this)."::set_cancel expensereport already with cancel status", LOG_WARNING);
1646  }
1647  return 0;
1648  }
1649 
1655  public function getNextNumRef()
1656  {
1657  global $langs, $conf;
1658  $langs->load("trips");
1659 
1660  if (!empty($conf->global->EXPENSEREPORT_ADDON)) {
1661  $mybool = false;
1662 
1663  $file = $conf->global->EXPENSEREPORT_ADDON.".php";
1664  $classname = $conf->global->EXPENSEREPORT_ADDON;
1665 
1666  // Include file with class
1667  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
1668  foreach ($dirmodels as $reldir) {
1669  $dir = dol_buildpath($reldir."core/modules/expensereport/");
1670 
1671  // Load file with numbering class (if found)
1672  $mybool |= @include_once $dir.$file;
1673  }
1674 
1675  if ($mybool === false) {
1676  dol_print_error('', "Failed to include file ".$file);
1677  return '';
1678  }
1679 
1680  $obj = new $classname();
1681  $numref = $obj->getNextValue($this);
1682 
1683  if ($numref != "") {
1684  return $numref;
1685  } else {
1686  $this->error = $obj->error;
1687  $this->errors = $obj->errors;
1688  //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
1689  return -1;
1690  }
1691  } else {
1692  $this->error = "Error_EXPENSEREPORT_ADDON_NotDefined";
1693  return -2;
1694  }
1695  }
1696 
1704  public function getTooltipContentArray($params)
1705  {
1706  global $conf, $langs;
1707 
1708  $langs->load('expensereport');
1709 
1710  $nofetch = !empty($params['nofetch']);
1711  $moretitle = $params['moretitle'] ?? '';
1712 
1713  $datas = array();
1714  $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("ExpenseReport").'</u>';
1715  if (isset($this->status)) {
1716  $datas['picto'] .= ' '.$this->getLibStatut(5);
1717  }
1718  if ($moretitle) {
1719  $datas['picto'] .= ' - '.$moretitle;
1720  }
1721  if (!empty($this->ref)) {
1722  $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1723  }
1724  if (!empty($this->total_ht)) {
1725  $datas['total_ht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
1726  }
1727  if (!empty($this->total_tva)) {
1728  $datas['total_tva'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
1729  }
1730  if (!empty($this->total_ttc)) {
1731  $datas['total_ttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
1732  }
1733 
1734  return $datas;
1735  }
1736 
1749  public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $moretitle = '', $notooltip = 0, $save_lastsearch_value = -1)
1750  {
1751  global $langs, $conf, $hookmanager;
1752 
1753  $result = '';
1754 
1755  $url = DOL_URL_ROOT.'/expensereport/card.php?id='.$this->id;
1756 
1757  if ($short) {
1758  return $url;
1759  }
1760 
1761  $params = [
1762  'id' => $this->id,
1763  'objecttype' => $this->element,
1764  'option' => $option,
1765  'moretitle' => $moretitle,
1766  'nofetch' => 1,
1767  ];
1768  $classfortooltip = 'classfortooltip';
1769  $dataparams = '';
1770  if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1771  $classfortooltip = 'classforajaxtooltip';
1772  $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
1773  $label = '';
1774  } else {
1775  $label = implode($this->getTooltipContentArray($params));
1776  }
1777 
1778  if ($option != 'nolink') {
1779  // Add param to save lastsearch_values or not
1780  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1781  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1782  $add_save_lastsearch_values = 1;
1783  }
1784  if ($add_save_lastsearch_values) {
1785  $url .= '&save_lastsearch_values=1';
1786  }
1787  }
1788 
1789  $ref = $this->ref;
1790  if (empty($ref)) {
1791  $ref = $this->id;
1792  }
1793 
1794  $linkclose = '';
1795  if (empty($notooltip)) {
1796  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
1797  $label = $langs->trans("ShowExpenseReport");
1798  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1799  }
1800  $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
1801  $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
1802  }
1803 
1804  $linkstart = '<a href="'.$url.'"';
1805  $linkstart .= $linkclose.'>';
1806  $linkend = '</a>';
1807 
1808  $result .= $linkstart;
1809  if ($withpicto) {
1810  $result .= img_object(($notooltip ? '' : $label), $this->picto, ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : $dataparams.' class="'.(($withpicto != 2) ? 'paddingright ' : '').$classfortooltip.'"'), 0, 0, $notooltip ? 0 : 1);
1811  }
1812  if ($withpicto != 2) {
1813  $result .= ($max ? dol_trunc($ref, $max) : $ref);
1814  }
1815  $result .= $linkend;
1816 
1817  global $action;
1818  $hookmanager->initHooks(array($this->element . 'dao'));
1819  $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
1820  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1821  if ($reshook > 0) {
1822  $result = $hookmanager->resPrint;
1823  } else {
1824  $result .= $hookmanager->resPrint;
1825  }
1826  return $result;
1827  }
1828 
1829  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1837  public function update_totaux_add($ligne_total_ht, $ligne_total_tva)
1838  {
1839  // phpcs:enable
1840  $this->total_ht = $this->total_ht + $ligne_total_ht;
1841  $this->total_tva = $this->total_tva + $ligne_total_tva;
1842  $this->total_ttc = $this->total_ht + $this->total_tva;
1843 
1844  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
1845  $sql .= " total_ht = ".$this->total_ht;
1846  $sql .= " , total_ttc = ".$this->total_ttc;
1847  $sql .= " , total_tva = ".$this->total_tva;
1848  $sql .= " WHERE rowid = ".((int) $this->id);
1849 
1850  $result = $this->db->query($sql);
1851  if ($result) {
1852  return 1;
1853  } else {
1854  $this->error = $this->db->error();
1855  return -1;
1856  }
1857  }
1858 
1874  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)
1875  {
1876  global $conf, $langs, $mysoc;
1877 
1878  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);
1879 
1880  if ($this->status == self::STATUS_DRAFT) {
1881  if (empty($qty)) {
1882  $qty = 0;
1883  }
1884  if (empty($fk_c_type_fees) || $fk_c_type_fees < 0) {
1885  $fk_c_type_fees = 0;
1886  }
1887  if (empty($fk_c_exp_tax_cat) || $fk_c_exp_tax_cat < 0) {
1888  $fk_c_exp_tax_cat = 0;
1889  }
1890  if (empty($vatrate) || $vatrate < 0) {
1891  $vatrate = 0;
1892  }
1893  if (empty($date)) {
1894  $date = '';
1895  }
1896  if (empty($fk_project)) {
1897  $fk_project = 0;
1898  }
1899 
1900  $qty = price2num($qty);
1901  if (!preg_match('/\s*\‍((.*)\‍)/', $vatrate)) {
1902  $vatrate = price2num($vatrate); // $txtva can have format '5.0 (XXX)' or '5'
1903  }
1904  $up = price2num($up);
1905 
1906  $this->db->begin();
1907 
1908  $this->line = new ExpenseReportLine($this->db);
1909 
1910  // We don't know seller and buyer for expense reports
1911  $seller = $mysoc; // We use same than current company (expense report are often done in same country)
1912  $seller->tva_assuj = 1; // Most seller uses vat
1913  $buyer = new Societe($this->db);
1914 
1915  $localtaxes_type = getLocalTaxesFromRate($vatrate, 0, $buyer, $seller);
1916 
1917  $vat_src_code = '';
1918  $reg = array();
1919  if (preg_match('/\s*\‍((.*)\‍)/', $vatrate, $reg)) {
1920  $vat_src_code = $reg[1];
1921  $vatrate = preg_replace('/\s*\‍(.*\‍)/', '', $vatrate); // Remove code into vatrate.
1922  }
1923  $vatrate = preg_replace('/\*/', '', $vatrate);
1924 
1925  $tmp = calcul_price_total($qty, $up, 0, $vatrate, -1, -1, 0, 'TTC', 0, $type, $seller, $localtaxes_type);
1926 
1927  $this->line->value_unit = $up;
1928 
1929  $this->line->vat_src_code = $vat_src_code;
1930  $this->line->vatrate = price2num($vatrate);
1931  $this->line->localtax1_tx = $localtaxes_type[1];
1932  $this->line->localtax2_tx = $localtaxes_type[3];
1933  $this->line->localtax1_type = $localtaxes_type[0];
1934  $this->line->localtax2_type = $localtaxes_type[2];
1935 
1936  $this->line->total_ttc = $tmp[2];
1937  $this->line->total_ht = $tmp[0];
1938  $this->line->total_tva = $tmp[1];
1939  $this->line->total_localtax1 = $tmp[9];
1940  $this->line->total_localtax2 = $tmp[10];
1941 
1942  $this->line->fk_expensereport = $this->id;
1943  $this->line->qty = $qty;
1944  $this->line->date = $date;
1945  $this->line->fk_c_type_fees = $fk_c_type_fees;
1946  $this->line->fk_c_exp_tax_cat = $fk_c_exp_tax_cat;
1947  $this->line->comments = $comments;
1948  $this->line->fk_projet = $fk_project; // deprecated
1949  $this->line->fk_project = $fk_project;
1950 
1951  $this->line->fk_ecm_files = $fk_ecm_files;
1952 
1953  $this->applyOffset();
1954  $this->checkRules($type, $seller);
1955 
1956  $result = $this->line->insert(0, true);
1957  if ($result > 0) {
1958  $result = $this->update_price(1); // This method is designed to add line from user input so total calculation must be done using 'auto' mode.
1959  if ($result > 0) {
1960  $this->db->commit();
1961  return $this->line->id;
1962  } else {
1963  $this->db->rollback();
1964  return -1;
1965  }
1966  } else {
1967  $this->error = $this->line->error;
1968  dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
1969  $this->db->rollback();
1970  return -2;
1971  }
1972  } else {
1973  dol_syslog(get_class($this)."::addline status of expense report must be Draft to allow use of ->addline()", LOG_ERR);
1974  $this->error = 'ErrorExpenseNotDraft';
1975  return -3;
1976  }
1977  }
1978 
1986  public function checkRules($type = 0, $seller = '')
1987  {
1988  global $user, $conf, $db, $langs, $mysoc;
1989 
1990  $langs->load('trips');
1991 
1992  // We don't know seller and buyer for expense reports
1993  if (!is_object($seller)) {
1994  $seller = $mysoc; // We use same than current company (expense report are often done in same country)
1995  $seller->tva_assuj = 1; // Most seller uses vat
1996  }
1997 
1998  $expensereportrule = new ExpenseReportRule($db);
1999  $rulestocheck = $expensereportrule->getAllRule($this->line->fk_c_type_fees, $this->line->date, $this->fk_user_author);
2000 
2001  $violation = 0;
2002  $rule_warning_message_tab = array();
2003 
2004  $current_total_ttc = $this->line->total_ttc;
2005  $new_current_total_ttc = $this->line->total_ttc;
2006 
2007  // check if one is violated
2008  foreach ($rulestocheck as $rule) {
2009  if (in_array($rule->code_expense_rules_type, array('EX_DAY', 'EX_MON', 'EX_YEA'))) {
2010  $amount_to_test = $this->line->getExpAmount($rule, $this->fk_user_author, $rule->code_expense_rules_type);
2011  } else {
2012  $amount_to_test = $current_total_ttc; // EX_EXP
2013  }
2014 
2015  $amount_to_test = $amount_to_test - $current_total_ttc + $new_current_total_ttc; // if amount as been modified by a previous rule
2016 
2017  if ($amount_to_test > $rule->amount) {
2018  $violation++;
2019 
2020  if ($rule->restrictive) {
2021  $this->error = 'ExpenseReportConstraintViolationError';
2022  $this->errors[] = $this->error;
2023 
2024  $new_current_total_ttc -= $amount_to_test - $rule->amount; // ex, entered 16€, limit 12€, subtracts 4€;
2025  $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));
2026  } else {
2027  $this->error = 'ExpenseReportConstraintViolationWarning';
2028  $this->errors[] = $this->error;
2029 
2030  $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));
2031  }
2032 
2033  // No break, we sould test if another rule is violated
2034  }
2035  }
2036 
2037  $this->line->rule_warning_message = implode('\n', $rule_warning_message_tab);
2038 
2039  if ($violation > 0) {
2040  $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);
2041 
2042  $this->line->value_unit = $tmp[5];
2043  $this->line->total_ttc = $tmp[2];
2044  $this->line->total_ht = $tmp[0];
2045  $this->line->total_tva = $tmp[1];
2046  $this->line->total_localtax1 = $tmp[9];
2047  $this->line->total_localtax2 = $tmp[10];
2048 
2049  return false;
2050  } else {
2051  return true;
2052  }
2053  }
2054 
2062  public function applyOffset($type = 0, $seller = '')
2063  {
2064  global $conf, $mysoc;
2065 
2066  if (empty($conf->global->MAIN_USE_EXPENSE_IK)) {
2067  return false;
2068  }
2069 
2070  $userauthor = new User($this->db);
2071  if ($userauthor->fetch($this->fk_user_author) <= 0) {
2072  $this->error = 'ErrorCantFetchUser';
2073  $this->errors[] = 'ErrorCantFetchUser';
2074  return false;
2075  }
2076 
2077  // We don't know seller and buyer for expense reports
2078  if (!is_object($seller)) {
2079  $seller = $mysoc; // We use same than current company (expense report are often done in same country)
2080  $seller->tva_assuj = 1; // Most seller uses vat
2081  }
2082 
2083  $expenseik = new ExpenseReportIk($this->db);
2084  $range = $expenseik->getRangeByUser($userauthor, $this->line->fk_c_exp_tax_cat);
2085 
2086  if (empty($range)) {
2087  $this->error = 'ErrorNoRangeAvailable';
2088  $this->errors[] = 'ErrorNoRangeAvailable';
2089  return false;
2090  }
2091 
2092  if (!empty($conf->global->MAIN_EXPENSE_APPLY_ENTIRE_OFFSET)) {
2093  $ikoffset = $range->ikoffset;
2094  } else {
2095  $ikoffset = $range->ikoffset / 12; // The amount of offset is a global value for the year
2096  }
2097 
2098  // Test if ikoffset has been applied for the current month
2099  if (!$this->offsetAlreadyGiven()) {
2100  $new_up = $range->coef + ($ikoffset / $this->line->qty);
2101  $tmp = calcul_price_total($this->line->qty, $new_up, 0, $this->line->vatrate, 0, 0, 0, 'TTC', 0, $type, $seller);
2102 
2103  $this->line->value_unit = $tmp[5];
2104  $this->line->total_ttc = $tmp[2];
2105  $this->line->total_ht = $tmp[0];
2106  $this->line->total_tva = $tmp[1];
2107  $this->line->total_localtax1 = $tmp[9];
2108  $this->line->total_localtax2 = $tmp[10];
2109 
2110  return true;
2111  }
2112 
2113  return false;
2114  }
2115 
2121  public function offsetAlreadyGiven()
2122  {
2123  $sql = 'SELECT e.rowid FROM '.MAIN_DB_PREFIX.'expensereport e';
2124  $sql .= " INNER JOIN ".MAIN_DB_PREFIX."expensereport_det d ON (e.rowid = d.fk_expensereport)";
2125  $sql .= " INNER JOIN ".MAIN_DB_PREFIX."c_type_fees f ON (d.fk_c_type_fees = f.id AND f.code = 'EX_KME')";
2126  $sql .= " WHERE e.fk_user_author = ".(int) $this->fk_user_author;
2127  $sql .= " AND YEAR(d.date) = '".dol_print_date($this->line->date, '%Y')."' AND MONTH(d.date) = '".dol_print_date($this->line->date, '%m')."'";
2128  if (!empty($this->line->id)) {
2129  $sql .= ' AND d.rowid <> '.((int) $this->line->id);
2130  }
2131 
2132  dol_syslog(get_class($this)."::offsetAlreadyGiven");
2133  $resql = $this->db->query($sql);
2134  if ($resql) {
2135  $num = $this->db->num_rows($resql);
2136  if ($num > 0) {
2137  return true;
2138  }
2139  } else {
2140  dol_print_error($this->db);
2141  }
2142 
2143  return false;
2144  }
2145 
2163  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)
2164  {
2165  global $user, $mysoc;
2166 
2167  if ($this->status == self::STATUS_DRAFT || $this->status == self::STATUS_REFUSED) {
2168  $this->db->begin();
2169 
2170  $error = 0;
2171  $type = 0; // TODO What if type is service ?
2172 
2173  // We don't know seller and buyer for expense reports
2174  $seller = $mysoc; // We use same than current company (expense report are often done in same country)
2175  $seller->tva_assuj = 1; // Most seller uses vat
2176  $seller->localtax1_assuj = $mysoc->localtax1_assuj; // We don't know, we reuse the state of company
2177  $seller->localtax2_assuj = $mysoc->localtax1_assuj; // We don't know, we reuse the state of company
2178  $buyer = new Societe($this->db);
2179 
2180  $localtaxes_type = getLocalTaxesFromRate($vatrate, 0, $buyer, $seller);
2181 
2182  // Clean vat code
2183  $reg = array();
2184  $vat_src_code = '';
2185  if (preg_match('/\‍((.*)\‍)/', $vatrate, $reg)) {
2186  $vat_src_code = $reg[1];
2187  $vatrate = preg_replace('/\s*\‍(.*\‍)/', '', $vatrate); // Remove code into vatrate.
2188  }
2189  $vatrate = preg_replace('/\*/', '', $vatrate);
2190 
2191  $tmp = calcul_price_total($qty, $value_unit, 0, $vatrate, -1, -1, 0, 'TTC', 0, $type, $seller, $localtaxes_type);
2192  //var_dump($vatrate);var_dump($localtaxes_type);var_dump($tmp);exit;
2193  // calcul total of line
2194  //$total_ttc = price2num($qty*$value_unit, 'MT');
2195 
2196  $tx_tva = $vatrate / 100;
2197  $tx_tva = $tx_tva + 1;
2198 
2199  $this->line = new ExpenseReportLine($this->db);
2200  $this->line->comments = $comments;
2201  $this->line->qty = $qty;
2202  $this->line->value_unit = $value_unit;
2203  $this->line->date = $date;
2204 
2205  $this->line->fk_expensereport = $expensereport_id;
2206  $this->line->fk_c_type_fees = $type_fees_id;
2207  $this->line->fk_c_exp_tax_cat = $fk_c_exp_tax_cat;
2208  $this->line->fk_projet = $projet_id; // deprecated
2209  $this->line->fk_project = $projet_id;
2210 
2211  $this->line->vat_src_code = $vat_src_code;
2212  $this->line->vatrate = price2num($vatrate);
2213  $this->line->localtax1_tx = $localtaxes_type[1];
2214  $this->line->localtax2_tx = $localtaxes_type[3];
2215  $this->line->localtax1_type = $localtaxes_type[0];
2216  $this->line->localtax2_type = $localtaxes_type[2];
2217 
2218  $this->line->total_ttc = $tmp[2];
2219  $this->line->total_ht = $tmp[0];
2220  $this->line->total_tva = $tmp[1];
2221  $this->line->total_localtax1 = $tmp[9];
2222  $this->line->total_localtax2 = $tmp[10];
2223 
2224  $this->line->fk_ecm_files = $fk_ecm_files;
2225 
2226  $this->line->id = ((int) $rowid);
2227 
2228  // Select des infos sur le type fees
2229  $sql = "SELECT c.code as code_type_fees, c.label as libelle_type_fees";
2230  $sql .= " FROM ".MAIN_DB_PREFIX."c_type_fees as c";
2231  $sql .= " WHERE c.id = ".((int) $type_fees_id);
2232  $resql = $this->db->query($sql);
2233  if ($resql) {
2234  $objp_fees = $this->db->fetch_object($resql);
2235  $this->line->type_fees_code = $objp_fees->code_type_fees;
2236  $this->line->type_fees_libelle = $objp_fees->libelle_type_fees;
2237  $this->db->free($resql);
2238  }
2239 
2240  // Select des informations du projet
2241  $sql = "SELECT p.ref as ref_projet, p.title as title_projet";
2242  $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
2243  $sql .= " WHERE p.rowid = ".((int) $projet_id);
2244  $resql = $this->db->query($sql);
2245  if ($resql) {
2246  $objp_projet = $this->db->fetch_object($resql);
2247  $this->line->projet_ref = $objp_projet->ref_projet;
2248  $this->line->projet_title = $objp_projet->title_projet;
2249  $this->db->free($resql);
2250  }
2251 
2252  $this->applyOffset();
2253  $this->checkRules();
2254 
2255  $result = $this->line->update($user);
2256  if ($result < 0) {
2257  $error++;
2258  }
2259 
2260  if (!$error && !$notrigger) {
2261  // Call triggers
2262  $result = $this->call_trigger('EXPENSE_REPORT_DET_MODIFY', $user);
2263  if ($result < 0) {
2264  $error++;
2265  }
2266  // End call triggers
2267  }
2268 
2269  if (!$error) {
2270  $this->db->commit();
2271  return 1;
2272  } else {
2273  $this->error = $this->line->error;
2274  $this->errors = $this->line->errors;
2275  $this->db->rollback();
2276  return -2;
2277  }
2278  }
2279 
2280  return 0;
2281  }
2282 
2291  public function deleteline($rowid, $fuser = '', $notrigger = 0)
2292  {
2293  $error=0;
2294 
2295  $this->db->begin();
2296 
2297  if (!$notrigger) {
2298  // Call triggers
2299  $result = $this->call_trigger('EXPENSE_REPORT_DET_DELETE', $fuser);
2300  if ($result < 0) {
2301  $error++;
2302  }
2303  // End call triggers
2304  }
2305 
2306  $sql = ' DELETE FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2307  $sql .= ' WHERE rowid = '.((int) $rowid);
2308 
2309  dol_syslog(get_class($this)."::deleteline sql=".$sql);
2310  $result = $this->db->query($sql);
2311 
2312  if (!$result || $error > 0 ) {
2313  $this->error = $this->db->error();
2314  dol_syslog(get_class($this)."::deleteline Error ".$this->error, LOG_ERR);
2315  $this->db->rollback();
2316  return -1;
2317  }
2318 
2319  $this->update_price(1);
2320 
2321  $this->db->commit();
2322 
2323  return 1;
2324  }
2325 
2326  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2335  public function periode_existe($fuser, $date_debut, $date_fin)
2336  {
2337  // phpcs:enable
2338  $sql = "SELECT rowid, date_debut, date_fin";
2339  $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element;
2340  $sql .= " WHERE fk_user_author = ".((int) $fuser->id);
2341 
2342  dol_syslog(get_class($this)."::periode_existe sql=".$sql);
2343  $result = $this->db->query($sql);
2344  if ($result) {
2345  $num_rows = $this->db->num_rows($result); $i = 0;
2346 
2347  if ($num_rows > 0) {
2348  $date_d_form = $date_debut;
2349  $date_f_form = $date_fin;
2350 
2351  while ($i < $num_rows) {
2352  $objp = $this->db->fetch_object($result);
2353 
2354  $date_d_req = $this->db->jdate($objp->date_debut); // 3
2355  $date_f_req = $this->db->jdate($objp->date_fin); // 4
2356 
2357  if (!($date_f_form < $date_d_req || $date_d_form > $date_f_req)) {
2358  return $objp->rowid;
2359  }
2360 
2361  $i++;
2362  }
2363 
2364  return 0;
2365  } else {
2366  return 0;
2367  }
2368  } else {
2369  $this->error = $this->db->lasterror();
2370  dol_syslog(get_class($this)."::periode_existe Error ".$this->error, LOG_ERR);
2371  return -1;
2372  }
2373  }
2374 
2375 
2376  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2384  {
2385  // phpcs:enable
2386  $users_validator = array();
2387 
2388  $sql = "SELECT DISTINCT ur.fk_user";
2389  $sql .= " FROM ".MAIN_DB_PREFIX."user_rights as ur, ".MAIN_DB_PREFIX."rights_def as rd";
2390  $sql .= " WHERE ur.fk_id = rd.id and rd.module = 'expensereport' AND rd.perms = 'approve'"; // Permission 'Approve';
2391  $sql .= " UNION";
2392  $sql .= " SELECT DISTINCT ugu.fk_user";
2393  $sql .= " FROM ".MAIN_DB_PREFIX."usergroup_user as ugu, ".MAIN_DB_PREFIX."usergroup_rights as ur, ".MAIN_DB_PREFIX."rights_def as rd";
2394  $sql .= " WHERE ugu.fk_usergroup = ur.fk_usergroup AND ur.fk_id = rd.id and rd.module = 'expensereport' AND rd.perms = 'approve'"; // Permission 'Approve';
2395  //print $sql;
2396 
2397  dol_syslog(get_class($this)."::fetch_users_approver_expensereport sql=".$sql);
2398  $result = $this->db->query($sql);
2399  if ($result) {
2400  $num_rows = $this->db->num_rows($result); $i = 0;
2401  while ($i < $num_rows) {
2402  $objp = $this->db->fetch_object($result);
2403  array_push($users_validator, $objp->fk_user);
2404  $i++;
2405  }
2406  return $users_validator;
2407  } else {
2408  $this->error = $this->db->lasterror();
2409  dol_syslog(get_class($this)."::fetch_users_approver_expensereport Error ".$this->error, LOG_ERR);
2410  return -1;
2411  }
2412  }
2413 
2425  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2426  {
2427  global $conf;
2428 
2429  $outputlangs->load("trips");
2430 
2431  if (!dol_strlen($modele)) {
2432  if (!empty($this->model_pdf)) {
2433  $modele = $this->model_pdf;
2434  } elseif (!empty($this->modelpdf)) { // deprecated
2435  $modele = $this->modelpdf;
2436  } elseif (!empty($conf->global->EXPENSEREPORT_ADDON_PDF)) {
2437  $modele = $conf->global->EXPENSEREPORT_ADDON_PDF;
2438  }
2439  }
2440 
2441  if (!empty($modele)) {
2442  $modelpath = "core/modules/expensereport/doc/";
2443 
2444  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2445  } else {
2446  return 0;
2447  }
2448  }
2449 
2456  public function listOfTypes($active = 1)
2457  {
2458  global $langs;
2459  $ret = array();
2460  $sql = "SELECT id, code, label";
2461  $sql .= " FROM ".MAIN_DB_PREFIX."c_type_fees";
2462  $sql .= " WHERE active = ".((int) $active);
2463  dol_syslog(get_class($this)."::listOfTypes", LOG_DEBUG);
2464  $result = $this->db->query($sql);
2465  if ($result) {
2466  $num = $this->db->num_rows($result);
2467  $i = 0;
2468  while ($i < $num) {
2469  $obj = $this->db->fetch_object($result);
2470  $ret[$obj->code] = (($langs->transnoentitiesnoconv($obj->code) != $obj->code) ? $langs->transnoentitiesnoconv($obj->code) : $obj->label);
2471  $i++;
2472  }
2473  } else {
2474  dol_print_error($this->db);
2475  }
2476  return $ret;
2477  }
2478 
2479  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2485  public function load_state_board()
2486  {
2487  // phpcs:enable
2488  global $conf, $user;
2489 
2490  $this->nb = array();
2491 
2492  $sql = "SELECT count(ex.rowid) as nb";
2493  $sql .= " FROM ".MAIN_DB_PREFIX."expensereport as ex";
2494  $sql .= " WHERE ex.fk_statut > 0";
2495  $sql .= " AND ex.entity IN (".getEntity('expensereport').")";
2496  if (empty($user->rights->expensereport->readall)) {
2497  $userchildids = $user->getAllChildIds(1);
2498  $sql .= " AND (ex.fk_user_author IN (".$this->db->sanitize(join(',', $userchildids)).")";
2499  $sql .= " OR ex.fk_user_validator IN (".$this->db->sanitize(join(',', $userchildids))."))";
2500  }
2501 
2502  $resql = $this->db->query($sql);
2503  if ($resql) {
2504  while ($obj = $this->db->fetch_object($resql)) {
2505  $this->nb["expensereports"] = $obj->nb;
2506  }
2507  $this->db->free($resql);
2508  return 1;
2509  } else {
2510  dol_print_error($this->db);
2511  $this->error = $this->db->error();
2512  return -1;
2513  }
2514  }
2515 
2516  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2524  public function load_board($user, $option = 'topay')
2525  {
2526  // phpcs:enable
2527  global $conf, $langs;
2528 
2529  if ($user->socid) {
2530  return -1; // protection pour eviter appel par utilisateur externe
2531  }
2532 
2533  $now = dol_now();
2534 
2535  $sql = "SELECT ex.rowid, ex.date_valid";
2536  $sql .= " FROM ".MAIN_DB_PREFIX."expensereport as ex";
2537  if ($option == 'toapprove') {
2538  $sql .= " WHERE ex.fk_statut = ".self::STATUS_VALIDATED;
2539  } else {
2540  $sql .= " WHERE ex.fk_statut = ".self::STATUS_APPROVED;
2541  }
2542  $sql .= " AND ex.entity IN (".getEntity('expensereport').")";
2543  if (empty($user->rights->expensereport->readall)) {
2544  $userchildids = $user->getAllChildIds(1);
2545  $sql .= " AND (ex.fk_user_author IN (".$this->db->sanitize(join(',', $userchildids)).")";
2546  $sql .= " OR ex.fk_user_validator IN (".$this->db->sanitize(join(',', $userchildids))."))";
2547  }
2548 
2549  $resql = $this->db->query($sql);
2550  if ($resql) {
2551  $langs->load("trips");
2552 
2553  $response = new WorkboardResponse();
2554  if ($option == 'toapprove') {
2555  $response->warning_delay = $conf->expensereport->approve->warning_delay / 60 / 60 / 24;
2556  $response->label = $langs->trans("ExpenseReportsToApprove");
2557  $response->labelShort = $langs->trans("ToApprove");
2558  $response->url = DOL_URL_ROOT.'/expensereport/list.php?mainmenu=hrm&amp;statut='.self::STATUS_VALIDATED;
2559  } else {
2560  $response->warning_delay = $conf->expensereport->payment->warning_delay / 60 / 60 / 24;
2561  $response->label = $langs->trans("ExpenseReportsToPay");
2562  $response->labelShort = $langs->trans("StatusToPay");
2563  $response->url = DOL_URL_ROOT.'/expensereport/list.php?mainmenu=hrm&amp;statut='.self::STATUS_APPROVED;
2564  }
2565  $response->img = img_object('', "trip");
2566 
2567  while ($obj = $this->db->fetch_object($resql)) {
2568  $response->nbtodo++;
2569 
2570  if ($option == 'toapprove') {
2571  if ($this->db->jdate($obj->date_valid) < ($now - $conf->expensereport->approve->warning_delay)) {
2572  $response->nbtodolate++;
2573  }
2574  } else {
2575  if ($this->db->jdate($obj->date_valid) < ($now - $conf->expensereport->payment->warning_delay)) {
2576  $response->nbtodolate++;
2577  }
2578  }
2579  }
2580 
2581  return $response;
2582  } else {
2583  dol_print_error($this->db);
2584  $this->error = $this->db->error();
2585  return -1;
2586  }
2587  }
2588 
2595  public function hasDelay($option)
2596  {
2597  global $conf;
2598 
2599  // Only valid expenses reports
2600  if ($option == 'toapprove' && $this->status != 2) {
2601  return false;
2602  }
2603  if ($option == 'topay' && $this->status != 5) {
2604  return false;
2605  }
2606 
2607  $now = dol_now();
2608  if ($option == 'toapprove') {
2609  return (!empty($this->datevalid) ? $this->datevalid : $this->date_valid) < ($now - $conf->expensereport->approve->warning_delay);
2610  } else {
2611  return (!empty($this->datevalid) ? $this->datevalid : $this->date_valid) < ($now - $conf->expensereport->payment->warning_delay);
2612  }
2613  }
2614 
2620  public function getVentilExportCompta()
2621  {
2622  $alreadydispatched = 0;
2623 
2624  $type = 'expense_report';
2625 
2626  $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);
2627  $resql = $this->db->query($sql);
2628  if ($resql) {
2629  $obj = $this->db->fetch_object($resql);
2630  if ($obj) {
2631  $alreadydispatched = $obj->nb;
2632  }
2633  } else {
2634  $this->error = $this->db->lasterror();
2635  return -1;
2636  }
2637 
2638  if ($alreadydispatched) {
2639  return 1;
2640  }
2641  return 0;
2642  }
2643 
2649  public function getSumPayments()
2650  {
2651  $table = 'payment_expensereport';
2652  $field = 'fk_expensereport';
2653 
2654  $sql = 'SELECT sum(amount) as amount';
2655  $sql .= ' FROM '.MAIN_DB_PREFIX.$table;
2656  $sql .= " WHERE ".$field." = ".((int) $this->id);
2657 
2658  dol_syslog(get_class($this)."::getSumPayments", LOG_DEBUG);
2659  $resql = $this->db->query($sql);
2660  if ($resql) {
2661  $obj = $this->db->fetch_object($resql);
2662  $this->db->free($resql);
2663  return (empty($obj->amount) ? 0 : $obj->amount);
2664  } else {
2665  $this->error = $this->db->lasterror();
2666  return -1;
2667  }
2668  }
2669 
2678  public function computeTotalKm($fk_cat, $qty, $tva)
2679  {
2680  global $langs, $db, $conf;
2681 
2682  $cumulYearQty = 0;
2683  $ranges = array();
2684  $coef = 0;
2685 
2686 
2687  if ($fk_cat < 0) {
2688  $this->error = $langs->trans('ErrorBadParameterCat');
2689  return -1;
2690  }
2691 
2692  if ($qty <= 0) {
2693  $this->error = $langs->trans('ErrorBadParameterQty');
2694  return -1;
2695  }
2696 
2697  $currentUser = new User($db);
2698  $currentUser->fetch($this->fk_user);
2699  $currentUser->getrights('expensereport');
2700  //Clean
2701  $qty = price2num($qty);
2702 
2703  $sql = " SELECT r.range_ik, t.ikoffset as offset, t.coef";
2704  $sql .= " FROM ".MAIN_DB_PREFIX."expensereport_ik t";
2705  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_exp_tax_range r ON r.rowid = t.fk_range";
2706  $sql .= " WHERE t.fk_c_exp_tax_cat = ".(int) $fk_cat;
2707  $sql .= " ORDER BY r.range_ik ASC";
2708 
2709  dol_syslog("expenseReport::computeTotalkm sql=".$sql, LOG_DEBUG);
2710 
2711  $result = $this->db->query($sql);
2712 
2713  if ($result) {
2714  if ($conf->global->EXPENSEREPORT_CALCULATE_MILEAGE_EXPENSE_COEFFICIENT_ON_CURRENT_YEAR) {
2715  $arrayDate = dol_getdate(dol_now());
2716  $sql = " SELECT count(n.qty) as cumul FROM ".MAIN_DB_PREFIX."expensereport_det n";
2717  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."expensereport e ON e.rowid = n.fk_expensereport";
2718  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_type_fees tf ON tf.id = n.fk_c_type_fees";
2719  $sql.= " WHERE e.fk_user_author = ".(int) $this->fk_user_author;
2720  $sql.= " AND YEAR(n.date) = ".(int) $arrayDate['year'];
2721  $sql.= " AND tf.code = 'EX_KME' ";
2722  $sql.= " AND e.fk_statut = ".(int) ExpenseReport::STATUS_VALIDATED;
2723 
2724  $resql = $this->db->query($sql);
2725 
2726  if ($resql) {
2727  $obj = $this->db->fetch_object($resql);
2728  $cumulYearQty = $obj->cumul;
2729  }
2730  $qty = $cumulYearQty + $qty;
2731  }
2732 
2733  $num = $this->db->num_rows($result);
2734 
2735  if ($num) {
2736  for ($i = 0; $i < $num; $i++) {
2737  $obj = $this->db->fetch_object($result);
2738 
2739  $ranges[$i] = $obj;
2740  }
2741 
2742 
2743  for ($i = 0; $i < $num; $i++) {
2744  if ($i < ($num - 1)) {
2745  if ($qty > $ranges[$i]->range_ik && $qty < $ranges[$i+1]->range_ik) {
2746  $coef = $ranges[$i]->coef;
2747  $offset = $ranges[$i]->offset;
2748  }
2749  } else {
2750  if ($qty > $ranges[$i]->range_ik) {
2751  $coef = $ranges[$i]->coef;
2752  $offset = $ranges[$i]->offset;
2753  }
2754  }
2755  }
2756  $total_ht = $coef;
2757  return $total_ht;
2758  } else {
2759  $this->error = $langs->trans('TaxUndefinedForThisCategory');
2760  return 0;
2761  }
2762  } else {
2763  $this->error = $this->db->error()." sql=".$sql;
2764 
2765  return -1;
2766  }
2767  }
2768 
2776  public function getKanbanView($option = '', $arraydata = null)
2777  {
2778  global $langs;
2779 
2780  $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2781 
2782  $return = '<div class="box-flex-item box-flex-grow-zero">';
2783  $return .= '<div class="info-box info-box-sm">';
2784  $return .= '<span class="info-box-icon bg-infobox-action">';
2785  $return .= img_picto('', $this->picto);
2786  $return .= '</span>';
2787  $return .= '<div class="info-box-content">';
2788  $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref).'</span>';
2789  $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
2790  if (property_exists($this, 'fk_user_author') && !empty($this->id)) {
2791  $return .= '<br><span class="info-box-label">'.$this->fk_user_author.'</span>';
2792  }
2793  if (property_exists($this, 'date_debut') && property_exists($this, 'date_fin')) {
2794  $return .= '<br><span class="info-box-label">'.dol_print_date($this->date_debut, 'day').'</span>';
2795  $return .= ' <span class="opacitymedium">'.$langs->trans("To").'</span> ';
2796  $return .= '<span class="info-box-label">'.dol_print_date($this->date_fin, 'day').'</span>';
2797  }
2798  if (method_exists($this, 'getLibStatut')) {
2799  $return .= '<br><div class="info-box-status margintoponly">'.$this->getLibStatut(3).'</div>';
2800  }
2801  $return .= '</div>';
2802  $return .= '</div>';
2803  $return .= '</div>';
2804  return $return;
2805  }
2806 }
2807 
2808 
2813 {
2817  public $db;
2818 
2822  public $table_element = 'expensereport_det';
2823 
2827  public $error = '';
2828 
2832  public $rowid;
2833 
2834  public $comments;
2835  public $qty;
2836  public $value_unit;
2837  public $date;
2838 
2842  public $fk_c_type_fees;
2843 
2847  public $fk_c_exp_tax_cat;
2848 
2852  public $fk_projet;
2853 
2857  public $fk_expensereport;
2858 
2859  public $type_fees_code;
2860  public $type_fees_libelle;
2861  public $type_fees_accountancy_code;
2862 
2863  public $projet_ref;
2864  public $projet_title;
2865  public $rang;
2866 
2867  public $vatrate;
2868  public $vat_src_code;
2869  public $tva_tx;
2870  public $localtax1_tx;
2871  public $localtax2_tx;
2872  public $localtax1_type;
2873  public $localtax2_type;
2874 
2875  public $total_ht;
2876  public $total_tva;
2877  public $total_ttc;
2878  public $total_localtax1;
2879  public $total_localtax2;
2880 
2881  // Multicurrency
2885  public $fk_multicurrency;
2886 
2890  public $multicurrency_code;
2891  public $multicurrency_tx;
2892  public $multicurrency_total_ht;
2893  public $multicurrency_total_tva;
2894  public $multicurrency_total_ttc;
2895 
2899  public $fk_ecm_files;
2900 
2901  public $rule_warning_message;
2902 
2903 
2909  public function __construct($db)
2910  {
2911  $this->db = $db;
2912  }
2913 
2920  public function fetch($rowid)
2921  {
2922  $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,';
2923  $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,';
2924  $sql .= ' fde.localtax1_tx, fde.localtax2_tx, fde.localtax1_type, fde.localtax2_type, fde.total_localtax1, fde.total_localtax2, fde.rule_warning_message,';
2925  $sql .= ' ctf.code as type_fees_code, ctf.label as type_fees_libelle,';
2926  $sql .= ' pjt.rowid as projet_id, pjt.title as projet_title, pjt.ref as projet_ref';
2927  $sql .= ' FROM '.MAIN_DB_PREFIX.'expensereport_det as fde';
2928  $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.
2929  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'projet as pjt ON fde.fk_projet=pjt.rowid';
2930  $sql .= ' WHERE fde.rowid = '.((int) $rowid);
2931 
2932  $result = $this->db->query($sql);
2933 
2934  if ($result) {
2935  $objp = $this->db->fetch_object($result);
2936 
2937  $this->rowid = $objp->rowid;
2938  $this->id = $objp->rowid;
2939  $this->ref = $objp->ref;
2940  $this->fk_expensereport = $objp->fk_expensereport;
2941  $this->comments = $objp->comments;
2942  $this->qty = $objp->qty;
2943  $this->date = $objp->date;
2944  $this->dates = $this->db->jdate($objp->date);
2945  $this->value_unit = $objp->value_unit;
2946  $this->fk_c_type_fees = $objp->fk_c_type_fees;
2947  $this->fk_c_exp_tax_cat = $objp->fk_c_exp_tax_cat;
2948  $this->fk_projet = $objp->fk_project; // deprecated
2949  $this->fk_project = $objp->fk_project;
2950  $this->type_fees_code = $objp->type_fees_code;
2951  $this->type_fees_libelle = $objp->type_fees_libelle;
2952  $this->projet_ref = $objp->projet_ref;
2953  $this->projet_title = $objp->projet_title;
2954 
2955  $this->vatrate = $objp->vatrate;
2956  $this->vat_src_code = $objp->vat_src_code;
2957  $this->localtax1_tx = $objp->localtax1_tx;
2958  $this->localtax2_tx = $objp->localtax2_tx;
2959  $this->localtax1_type = $objp->localtax1_type;
2960  $this->localtax2_type = $objp->localtax2_type;
2961 
2962  $this->total_ht = $objp->total_ht;
2963  $this->total_tva = $objp->total_tva;
2964  $this->total_ttc = $objp->total_ttc;
2965  $this->total_localtax1 = $objp->total_localtax1;
2966  $this->total_localtax2 = $objp->total_localtax2;
2967 
2968  $this->fk_ecm_files = $objp->fk_ecm_files;
2969 
2970  $this->rule_warning_message = $objp->rule_warning_message;
2971 
2972  $this->db->free($result);
2973 
2974  return $this->id;
2975  } else {
2976  dol_print_error($this->db);
2977  return -1;
2978  }
2979  }
2980 
2988  public function insert($notrigger = 0, $fromaddline = false)
2989  {
2990  global $user, $conf;
2991 
2992  $error = 0;
2993 
2994  dol_syslog("ExpenseReportLine::Insert", LOG_DEBUG);
2995 
2996  // Clean parameters
2997  $this->comments = trim($this->comments);
2998  if (empty($this->value_unit)) {
2999  $this->value_unit = 0;
3000  }
3001  $this->qty = price2num($this->qty);
3002  $this->vatrate = price2num($this->vatrate);
3003  if (empty($this->fk_c_exp_tax_cat)) {
3004  $this->fk_c_exp_tax_cat = 0;
3005  }
3006 
3007  $this->db->begin();
3008 
3009  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'expensereport_det';
3010  $sql .= ' (fk_expensereport, fk_c_type_fees, fk_projet,';
3011  $sql .= ' tva_tx, vat_src_code,';
3012  $sql .= ' localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3013  $sql .= ' comments, qty, value_unit,';
3014  $sql .= ' total_ht, total_tva, total_ttc,';
3015  $sql .= ' total_localtax1, total_localtax2,';
3016  $sql .= ' date, rule_warning_message, fk_c_exp_tax_cat, fk_ecm_files)';
3017  $sql .= " VALUES (".$this->db->escape($this->fk_expensereport).",";
3018  $sql .= " ".((int) $this->fk_c_type_fees).",";
3019  $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')).",";
3020  $sql .= " ".((float) $this->vatrate).",";
3021  $sql .= " '".$this->db->escape(empty($this->vat_src_code) ? '' : $this->vat_src_code)."',";
3022  $sql .= " ".((float) price2num($this->localtax1_tx)).",";
3023  $sql .= " ".((float) price2num($this->localtax2_tx)).",";
3024  $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3025  $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3026  $sql .= " '".$this->db->escape($this->comments)."',";
3027  $sql .= " ".((float) $this->qty).",";
3028  $sql .= " ".((float) $this->value_unit).",";
3029  $sql .= " ".((float) price2num($this->total_ht)).",";
3030  $sql .= " ".((float) price2num($this->total_tva)).",";
3031  $sql .= " ".((float) price2num($this->total_ttc)).",";
3032  $sql .= " ".((float) price2num($this->total_localtax1)).",";
3033  $sql .= " ".((float) price2num($this->total_localtax2)).",";
3034  $sql .= " '".$this->db->idate($this->date)."',";
3035  $sql .= " ".(empty($this->rule_warning_message) ? 'null' : "'".$this->db->escape($this->rule_warning_message)."'").",";
3036  $sql .= " ".((int) $this->fk_c_exp_tax_cat).",";
3037  $sql .= " ".($this->fk_ecm_files > 0 ? ((int) $this->fk_ecm_files) : 'null');
3038  $sql .= ")";
3039 
3040  $resql = $this->db->query($sql);
3041  if ($resql) {
3042  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'expensereport_det');
3043 
3044 
3045  if (!$error && !$notrigger) {
3046  // Call triggers
3047  $result = $this->call_trigger('EXPENSE_REPORT_DET_CREATE', $user);
3048  if ($result < 0) {
3049  $error++;
3050  }
3051  // End call triggers
3052  }
3053 
3054 
3055  if (!$fromaddline) {
3056  $tmpparent = new ExpenseReport($this->db);
3057  $tmpparent->fetch($this->fk_expensereport);
3058  $result = $tmpparent->update_price(1);
3059  if ($result < 0) {
3060  $error++;
3061  $this->error = $tmpparent->error;
3062  $this->errors = $tmpparent->errors;
3063  }
3064  }
3065  } else {
3066  $error++;
3067  }
3068 
3069  if (!$error) {
3070  $this->db->commit();
3071  return $this->id;
3072  } else {
3073  $this->error = $this->db->lasterror();
3074  dol_syslog("ExpenseReportLine::insert Error ".$this->error, LOG_ERR);
3075  $this->db->rollback();
3076  return -2;
3077  }
3078  }
3079 
3088  public function getExpAmount(ExpenseReportRule $rule, $fk_user, $mode = 'day')
3089  {
3090  $amount = 0;
3091 
3092  $sql = 'SELECT SUM(d.total_ttc) as total_amount';
3093  $sql .= ' FROM '.MAIN_DB_PREFIX.'expensereport_det d';
3094  $sql .= ' INNER JOIN '.MAIN_DB_PREFIX.'expensereport e ON (d.fk_expensereport = e.rowid)';
3095  $sql .= ' WHERE e.fk_user_author = '.((int) $fk_user);
3096  if (!empty($this->id)) {
3097  $sql .= ' AND d.rowid <> '.((int) $this->id);
3098  }
3099  $sql .= ' AND d.fk_c_type_fees = '.((int) $rule->fk_c_type_fees);
3100  if ($mode == 'day' || $mode == 'EX_DAY') {
3101  $sql .= " AND d.date = '".dol_print_date($this->date, '%Y-%m-%d')."'";
3102  } elseif ($mode == 'mon' || $mode == 'EX_MON') {
3103  $sql .= " AND DATE_FORMAT(d.date, '%Y-%m') = '".dol_print_date($this->date, '%Y-%m')."'"; // @todo DATE_FORMAT is forbidden
3104  } elseif ($mode == 'year' || $mode == 'EX_YEA') {
3105  $sql .= " AND DATE_FORMAT(d.date, '%Y') = '".dol_print_date($this->date, '%Y')."'"; // @todo DATE_FORMAT is forbidden
3106  }
3107 
3108  dol_syslog('ExpenseReportLine::getExpAmount');
3109 
3110  $resql = $this->db->query($sql);
3111  if ($resql) {
3112  $num = $this->db->num_rows($resql);
3113  if ($num > 0) {
3114  $obj = $this->db->fetch_object($resql);
3115  $amount = (double) $obj->total_amount;
3116  }
3117  } else {
3118  dol_print_error($this->db);
3119  }
3120 
3121  return $amount + $this->total_ttc;
3122  }
3123 
3130  public function update(User $user)
3131  {
3132  global $langs, $conf;
3133 
3134  $error = 0;
3135 
3136  // Clean parameters
3137  $this->comments = trim($this->comments);
3138  $this->vatrate = price2num($this->vatrate);
3139  $this->value_unit = price2num($this->value_unit);
3140  if (empty($this->fk_c_exp_tax_cat)) {
3141  $this->fk_c_exp_tax_cat = 0;
3142  }
3143 
3144  $this->db->begin();
3145 
3146  // Update line in database
3147  $sql = "UPDATE ".MAIN_DB_PREFIX."expensereport_det SET";
3148  $sql .= " comments='".$this->db->escape($this->comments)."'";
3149  $sql .= ", value_unit = ".((float) $this->value_unit);
3150  $sql .= ", qty=".((float) $this->qty);
3151  $sql .= ", date='".$this->db->idate($this->date)."'";
3152  $sql .= ", total_ht=".((float) price2num($this->total_ht, 'MT'));
3153  $sql .= ", total_tva=".((float) price2num($this->total_tva, 'MT'));
3154  $sql .= ", total_ttc=".((float) price2num($this->total_ttc, 'MT'));
3155  $sql .= ", total_localtax1=".((float) price2num($this->total_localtax1, 'MT'));
3156  $sql .= ", total_localtax2=".((float) price2num($this->total_localtax2, 'MT'));
3157  $sql .= ", tva_tx=".((float) $this->vatrate);
3158  $sql .= ", vat_src_code='".$this->db->escape($this->vat_src_code)."'";
3159  $sql .= ", localtax1_tx=".((float) $this->localtax1_tx);
3160  $sql .= ", localtax2_tx=".((float) $this->localtax2_tx);
3161  $sql .= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'";
3162  $sql .= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'";
3163  $sql .= ", rule_warning_message='".$this->db->escape($this->rule_warning_message)."'";
3164  $sql .= ", fk_c_exp_tax_cat=".$this->db->escape($this->fk_c_exp_tax_cat);
3165  $sql .= ", fk_ecm_files=".($this->fk_ecm_files > 0 ? ((int) $this->fk_ecm_files) : 'null');
3166  if ($this->fk_c_type_fees) {
3167  $sql .= ", fk_c_type_fees = ".((int) $this->fk_c_type_fees);
3168  } else {
3169  $sql .= ", fk_c_type_fees=null";
3170  }
3171  if ($this->fk_project > 0) {
3172  $sql .= ", fk_projet=".((int) $this->fk_project);
3173  } else {
3174  $sql .= ", fk_projet=null";
3175  }
3176  $sql .= " WHERE rowid = ".((int) ($this->rowid ? $this->rowid : $this->id));
3177 
3178  dol_syslog("ExpenseReportLine::update");
3179 
3180  $resql = $this->db->query($sql);
3181  if ($resql) {
3182  $tmpparent = new ExpenseReport($this->db);
3183  $result = $tmpparent->fetch($this->fk_expensereport);
3184  if ($result > 0) {
3185  $result = $tmpparent->update_price(1);
3186  if ($result < 0) {
3187  $error++;
3188  $this->error = $tmpparent->error;
3189  $this->errors = $tmpparent->errors;
3190  }
3191  } else {
3192  $error++;
3193  $this->error = $tmpparent->error;
3194  $this->errors = $tmpparent->errors;
3195  }
3196  } else {
3197  $error++;
3198  dol_print_error($this->db);
3199  }
3200 
3201  if (!$error) {
3202  $this->db->commit();
3203  return 1;
3204  } else {
3205  $this->error = $this->db->lasterror();
3206  dol_syslog("ExpenseReportLine::update Error ".$this->error, LOG_ERR);
3207  $this->db->rollback();
3208  return -2;
3209  }
3210  }
3211 
3212  // ajouter ici comput_ ...
3213 }
$object ref
Definition: info.php:78
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.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
setErrorsFromObject($object)
setErrorsFromObject
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid='', $f_user=null, $notrigger=0)
Delete all links between an object $this.
update_price($exclspec=0, $roundingadjust='none', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
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.
__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 vehicule categor...
load_state_board()
Charge indicateurs this->nb pour le tableau de bord.
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.
deleteline($rowid, $fuser='', $notrigger=0)
deleteline
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.
setDeny($fuser, $details, $notrigger=0)
setDeny
getVentilExportCompta()
Return if object was dispatched into bookkeeping.
getKanbanView($option='', $arraydata=null)
Return clicable link of object (with eventually 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 accordign 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.
fetch_line_by_project($projectid, $user='')
fetch_line_by_project
setUnpaid($fuser, $notrigger=0)
set_unpaid
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:48
if(isModEnabled('facture') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&empty($conf->global->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') &&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture') &&isModEnabled('commande') && $user->hasRight("commande", "lire") &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $sql
Social contributions to pay.
Definition: index.php:746
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:1485
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:1334
dol_dir_list($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:62
dol_delete_preview($object)
Delete all preview files linked to object instance.
Definition: files.lib.php:1537
vatrate($rate, $addpercent=false, $info_bits=0, $usestarfornpr=0, $html=0)
Return a string with VAT rate label formated for view output Used into pdf and HTML pages.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
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_print_date($time, $format='', $tzoutput='auto', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
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_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.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_getdate($timestamp, $fast=false, $forcetimezone='')
Return an array with locale date info.
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller='', $localtaxes_array='', $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition: price.lib.php:86