dolibarr  18.0.6
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 = ".((int) $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 = ".((int) $this->entity);
1326  $resql = $this->db->query($sql);
1327  if (!$resql) {
1328  $error++; $this->error = $this->db->lasterror();
1329  }
1330  $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filepath = 'expensereport/".$this->db->escape($this->newref)."'";
1331  $sql .= " WHERE filepath = 'expensereport/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1332  $resql = $this->db->query($sql);
1333  if (!$resql) {
1334  $error++; $this->error = $this->db->lasterror();
1335  }
1336 
1337  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1338  $oldref = dol_sanitizeFileName($this->ref);
1339  $newref = dol_sanitizeFileName($num);
1340  $dirsource = $conf->expensereport->multidir_output[$this->entity].'/'.$oldref;
1341  $dirdest = $conf->expensereport->multidir_output[$this->entity].'/'.$newref;
1342  if (!$error && file_exists($dirsource)) {
1343  dol_syslog(get_class($this)."::setValidate() rename dir ".$dirsource." into ".$dirdest);
1344 
1345  if (@rename($dirsource, $dirdest)) {
1346  dol_syslog("Rename ok");
1347  // Rename docs starting with $oldref with $newref
1348  $listoffiles = dol_dir_list($dirdest, 'files', 1, '^'.preg_quote($oldref, '/'));
1349  foreach ($listoffiles as $fileentry) {
1350  $dirsource = $fileentry['name'];
1351  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1352  $dirsource = $fileentry['path'].'/'.$dirsource;
1353  $dirdest = $fileentry['path'].'/'.$dirdest;
1354  @rename($dirsource, $dirdest);
1355  }
1356  }
1357  }
1358  }
1359  }
1360 
1361  // Set new ref and current status
1362  if (!$error) {
1363  $this->ref = $num;
1364  $this->status = self::STATUS_VALIDATED;
1365  }
1366 
1367  if (empty($error)) {
1368  $this->db->commit();
1369  return 1;
1370  } else {
1371  $this->db->rollback();
1372  $this->error = $this->db->error();
1373  return -2;
1374  }
1375  } else {
1376  $this->db->rollback();
1377  $this->error = $this->db->lasterror();
1378  return -1;
1379  }
1380  }
1381 
1382  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1389  public function set_save_from_refuse($fuser)
1390  {
1391  // phpcs:enable
1392  // Sélection de la date de début de la NDF
1393  $sql = 'SELECT date_debut';
1394  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element;
1395  $sql .= " WHERE rowid = ".((int) $this->id);
1396 
1397  $result = $this->db->query($sql);
1398 
1399  $objp = $this->db->fetch_object($result);
1400 
1401  $this->date_debut = $this->db->jdate($objp->date_debut);
1402 
1403  if ($this->status != self::STATUS_VALIDATED) {
1404  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1405  $sql .= " SET fk_statut = ".self::STATUS_VALIDATED;
1406  $sql .= " WHERE rowid = ".((int) $this->id);
1407 
1408  dol_syslog(get_class($this)."::set_save_from_refuse", LOG_DEBUG);
1409 
1410  if ($this->db->query($sql)) {
1411  return 1;
1412  } else {
1413  $this->error = $this->db->lasterror();
1414  return -1;
1415  }
1416  } else {
1417  dol_syslog(get_class($this)."::set_save_from_refuse expensereport already with save status", LOG_WARNING);
1418  }
1419 
1420  return 0;
1421  }
1422 
1430  public function setApproved($fuser, $notrigger = 0)
1431  {
1432  $now = dol_now();
1433  $error = 0;
1434 
1435  // date approval
1436  $this->date_approve = $now;
1437  if ($this->status != self::STATUS_APPROVED) {
1438  $this->db->begin();
1439 
1440  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1441  $sql .= " SET ref = '".$this->db->escape($this->ref)."', fk_statut = ".self::STATUS_APPROVED.", fk_user_approve = ".((int) $fuser->id).",";
1442  $sql .= " date_approve='".$this->db->idate($this->date_approve)."'";
1443  $sql .= " WHERE rowid = ".((int) $this->id);
1444  if ($this->db->query($sql)) {
1445  if (!$notrigger) {
1446  // Call trigger
1447  $result = $this->call_trigger('EXPENSE_REPORT_APPROVE', $fuser);
1448 
1449  if ($result < 0) {
1450  $error++;
1451  }
1452  // End call triggers
1453  }
1454 
1455  if (empty($error)) {
1456  $this->db->commit();
1457  return 1;
1458  } else {
1459  $this->db->rollback();
1460  $this->error = $this->db->error();
1461  return -2;
1462  }
1463  } else {
1464  $this->db->rollback();
1465  $this->error = $this->db->lasterror();
1466  return -1;
1467  }
1468  } else {
1469  dol_syslog(get_class($this)."::setApproved expensereport already with approve status", LOG_WARNING);
1470  }
1471 
1472  return 0;
1473  }
1474 
1483  public function setDeny($fuser, $details, $notrigger = 0)
1484  {
1485  $now = dol_now();
1486  $error = 0;
1487 
1488  // date de refus
1489  if ($this->status != self::STATUS_REFUSED) {
1490  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1491  $sql .= " SET ref = '".$this->db->escape($this->ref)."', fk_statut = ".self::STATUS_REFUSED.", fk_user_refuse = ".((int) $fuser->id).",";
1492  $sql .= " date_refuse='".$this->db->idate($now)."',";
1493  $sql .= " detail_refuse='".$this->db->escape($details)."',";
1494  $sql .= " fk_user_approve = NULL";
1495  $sql .= " WHERE rowid = ".((int) $this->id);
1496  if ($this->db->query($sql)) {
1497  $this->fk_statut = 99; // deprecated
1498  $this->status = 99;
1499  $this->fk_user_refuse = $fuser->id;
1500  $this->detail_refuse = $details;
1501  $this->date_refuse = $now;
1502 
1503  if (!$notrigger) {
1504  // Call trigger
1505  $result = $this->call_trigger('EXPENSE_REPORT_DENY', $fuser);
1506 
1507  if ($result < 0) {
1508  $error++;
1509  }
1510  // End call triggers
1511  }
1512 
1513  if (empty($error)) {
1514  $this->db->commit();
1515  return 1;
1516  } else {
1517  $this->db->rollback();
1518  $this->error = $this->db->error();
1519  return -2;
1520  }
1521  } else {
1522  $this->db->rollback();
1523  $this->error = $this->db->lasterror();
1524  return -1;
1525  }
1526  } else {
1527  dol_syslog(get_class($this)."::setDeny expensereport already with refuse status", LOG_WARNING);
1528  }
1529 
1530  return 0;
1531  }
1532 
1533  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1543  public function set_unpaid($fuser, $notrigger = 0)
1544  {
1545  // phpcs:enable
1546  dol_syslog(get_class($this)."::set_unpaid is deprecated, use setUnpaid instead", LOG_NOTICE);
1547  return $this->setUnpaid($fuser, $notrigger);
1548  }
1549 
1557  public function setUnpaid($fuser, $notrigger = 0)
1558  {
1559  $error = 0;
1560 
1561  if ($this->paid) {
1562  $this->db->begin();
1563 
1564  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1565  $sql .= " SET paid = 0, fk_statut = ".self::STATUS_APPROVED;
1566  $sql .= " WHERE rowid = ".((int) $this->id);
1567 
1568  dol_syslog(get_class($this)."::set_unpaid", LOG_DEBUG);
1569 
1570  if ($this->db->query($sql)) {
1571  if (!$notrigger) {
1572  // Call trigger
1573  $result = $this->call_trigger('EXPENSE_REPORT_UNPAID', $fuser);
1574 
1575  if ($result < 0) {
1576  $error++;
1577  }
1578  // End call triggers
1579  }
1580 
1581  if (empty($error)) {
1582  $this->db->commit();
1583  return 1;
1584  } else {
1585  $this->db->rollback();
1586  $this->error = $this->db->error();
1587  return -2;
1588  }
1589  } else {
1590  $this->db->rollback();
1591  $this->error = $this->db->error();
1592  return -1;
1593  }
1594  } else {
1595  dol_syslog(get_class($this)."::set_unpaid expensereport already with unpaid status", LOG_WARNING);
1596  }
1597 
1598  return 0;
1599  }
1600 
1601  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1610  public function set_cancel($fuser, $detail, $notrigger = 0)
1611  {
1612  // phpcs:enable
1613  $error = 0;
1614  $this->date_cancel = $this->db->idate(dol_now());
1615  if ($this->status != self::STATUS_CANCELED) {
1616  $this->db->begin();
1617 
1618  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1619  $sql .= " SET fk_statut = ".self::STATUS_CANCELED.", fk_user_cancel = ".((int) $fuser->id);
1620  $sql .= ", date_cancel='".$this->db->idate($this->date_cancel)."'";
1621  $sql .= ", detail_cancel='".$this->db->escape($detail)."'";
1622  $sql .= " WHERE rowid = ".((int) $this->id);
1623 
1624  dol_syslog(get_class($this)."::set_cancel", LOG_DEBUG);
1625 
1626  if ($this->db->query($sql)) {
1627  if (!$notrigger) {
1628  // Call trigger
1629  $result = $this->call_trigger('EXPENSE_REPORT_CANCEL', $fuser);
1630 
1631  if ($result < 0) {
1632  $error++;
1633  }
1634  // End call triggers
1635  }
1636 
1637  if (empty($error)) {
1638  $this->db->commit();
1639  return 1;
1640  } else {
1641  $this->db->rollback();
1642  $this->error = $this->db->error();
1643  return -2;
1644  }
1645  } else {
1646  $this->db->rollback();
1647  $this->error = $this->db->error();
1648  return -1;
1649  }
1650  } else {
1651  dol_syslog(get_class($this)."::set_cancel expensereport already with cancel status", LOG_WARNING);
1652  }
1653  return 0;
1654  }
1655 
1661  public function getNextNumRef()
1662  {
1663  global $langs, $conf;
1664  $langs->load("trips");
1665 
1666  if (!empty($conf->global->EXPENSEREPORT_ADDON)) {
1667  $mybool = false;
1668 
1669  $file = $conf->global->EXPENSEREPORT_ADDON.".php";
1670  $classname = $conf->global->EXPENSEREPORT_ADDON;
1671 
1672  // Include file with class
1673  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
1674  foreach ($dirmodels as $reldir) {
1675  $dir = dol_buildpath($reldir."core/modules/expensereport/");
1676 
1677  // Load file with numbering class (if found)
1678  $mybool |= @include_once $dir.$file;
1679  }
1680 
1681  if ($mybool === false) {
1682  dol_print_error('', "Failed to include file ".$file);
1683  return '';
1684  }
1685 
1686  $obj = new $classname();
1687  $numref = $obj->getNextValue($this);
1688 
1689  if ($numref != "") {
1690  return $numref;
1691  } else {
1692  $this->error = $obj->error;
1693  $this->errors = $obj->errors;
1694  //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
1695  return -1;
1696  }
1697  } else {
1698  $this->error = "Error_EXPENSEREPORT_ADDON_NotDefined";
1699  return -2;
1700  }
1701  }
1702 
1710  public function getTooltipContentArray($params)
1711  {
1712  global $conf, $langs;
1713 
1714  $langs->load('expensereport');
1715 
1716  $nofetch = !empty($params['nofetch']);
1717  $moretitle = $params['moretitle'] ?? '';
1718 
1719  $datas = array();
1720  $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("ExpenseReport").'</u>';
1721  if (isset($this->status)) {
1722  $datas['picto'] .= ' '.$this->getLibStatut(5);
1723  }
1724  if ($moretitle) {
1725  $datas['picto'] .= ' - '.$moretitle;
1726  }
1727  if (!empty($this->ref)) {
1728  $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1729  }
1730  if (!empty($this->total_ht)) {
1731  $datas['total_ht'] = '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
1732  }
1733  if (!empty($this->total_tva)) {
1734  $datas['total_tva'] = '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
1735  }
1736  if (!empty($this->total_ttc)) {
1737  $datas['total_ttc'] = '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
1738  }
1739 
1740  return $datas;
1741  }
1742 
1755  public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $moretitle = '', $notooltip = 0, $save_lastsearch_value = -1)
1756  {
1757  global $langs, $conf, $hookmanager;
1758 
1759  $result = '';
1760 
1761  $url = DOL_URL_ROOT.'/expensereport/card.php?id='.$this->id;
1762 
1763  if ($short) {
1764  return $url;
1765  }
1766 
1767  $params = [
1768  'id' => $this->id,
1769  'objecttype' => $this->element,
1770  'option' => $option,
1771  'moretitle' => $moretitle,
1772  'nofetch' => 1,
1773  ];
1774  $classfortooltip = 'classfortooltip';
1775  $dataparams = '';
1776  if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1777  $classfortooltip = 'classforajaxtooltip';
1778  $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
1779  $label = '';
1780  } else {
1781  $label = implode($this->getTooltipContentArray($params));
1782  }
1783 
1784  if ($option != 'nolink') {
1785  // Add param to save lastsearch_values or not
1786  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1787  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1788  $add_save_lastsearch_values = 1;
1789  }
1790  if ($add_save_lastsearch_values) {
1791  $url .= '&save_lastsearch_values=1';
1792  }
1793  }
1794 
1795  $ref = $this->ref;
1796  if (empty($ref)) {
1797  $ref = $this->id;
1798  }
1799 
1800  $linkclose = '';
1801  if (empty($notooltip)) {
1802  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
1803  $label = $langs->trans("ShowExpenseReport");
1804  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1805  }
1806  $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
1807  $linkclose .= $dataparams.' class="'.$classfortooltip.'"';
1808  }
1809 
1810  $linkstart = '<a href="'.$url.'"';
1811  $linkstart .= $linkclose.'>';
1812  $linkend = '</a>';
1813 
1814  $result .= $linkstart;
1815  if ($withpicto) {
1816  $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'"'), 0, 0, $notooltip ? 0 : 1);
1817  }
1818  if ($withpicto != 2) {
1819  $result .= ($max ? dol_trunc($ref, $max) : $ref);
1820  }
1821  $result .= $linkend;
1822 
1823  global $action;
1824  $hookmanager->initHooks(array($this->element . 'dao'));
1825  $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
1826  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1827  if ($reshook > 0) {
1828  $result = $hookmanager->resPrint;
1829  } else {
1830  $result .= $hookmanager->resPrint;
1831  }
1832  return $result;
1833  }
1834 
1835  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1843  public function update_totaux_add($ligne_total_ht, $ligne_total_tva)
1844  {
1845  // phpcs:enable
1846  $this->total_ht = $this->total_ht + $ligne_total_ht;
1847  $this->total_tva = $this->total_tva + $ligne_total_tva;
1848  $this->total_ttc = $this->total_ht + $this->total_tva;
1849 
1850  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
1851  $sql .= " total_ht = ".$this->total_ht;
1852  $sql .= " , total_ttc = ".$this->total_ttc;
1853  $sql .= " , total_tva = ".$this->total_tva;
1854  $sql .= " WHERE rowid = ".((int) $this->id);
1855 
1856  $result = $this->db->query($sql);
1857  if ($result) {
1858  return 1;
1859  } else {
1860  $this->error = $this->db->error();
1861  return -1;
1862  }
1863  }
1864 
1880  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)
1881  {
1882  global $conf, $langs, $mysoc;
1883 
1884  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);
1885 
1886  if ($this->status == self::STATUS_DRAFT) {
1887  if (empty($qty)) {
1888  $qty = 0;
1889  }
1890  if (empty($fk_c_type_fees) || $fk_c_type_fees < 0) {
1891  $fk_c_type_fees = 0;
1892  }
1893  if (empty($fk_c_exp_tax_cat) || $fk_c_exp_tax_cat < 0) {
1894  $fk_c_exp_tax_cat = 0;
1895  }
1896  if (empty($vatrate) || $vatrate < 0) {
1897  $vatrate = 0;
1898  }
1899  if (empty($date)) {
1900  $date = '';
1901  }
1902  if (empty($fk_project)) {
1903  $fk_project = 0;
1904  }
1905 
1906  $qty = price2num($qty);
1907  if (!preg_match('/\s*\‍((.*)\‍)/', $vatrate)) {
1908  $vatrate = price2num($vatrate); // $txtva can have format '5.0 (XXX)' or '5'
1909  }
1910  $up = price2num($up);
1911 
1912  $this->db->begin();
1913 
1914  $this->line = new ExpenseReportLine($this->db);
1915 
1916  // We don't know seller and buyer for expense reports
1917  $seller = $mysoc; // We use same than current company (expense report are often done in same country)
1918  $seller->tva_assuj = 1; // Most seller uses vat
1919  $buyer = new Societe($this->db);
1920 
1921  $localtaxes_type = getLocalTaxesFromRate($vatrate, 0, $buyer, $seller);
1922 
1923  $vat_src_code = '';
1924  $reg = array();
1925  if (preg_match('/\s*\‍((.*)\‍)/', $vatrate, $reg)) {
1926  $vat_src_code = $reg[1];
1927  $vatrate = preg_replace('/\s*\‍(.*\‍)/', '', $vatrate); // Remove code into vatrate.
1928  }
1929  $vatrate = preg_replace('/\*/', '', $vatrate);
1930 
1931  $tmp = calcul_price_total($qty, $up, 0, $vatrate, -1, -1, 0, 'TTC', 0, $type, $seller, $localtaxes_type);
1932 
1933  $this->line->value_unit = $up;
1934 
1935  $this->line->vat_src_code = $vat_src_code;
1936  $this->line->vatrate = price2num($vatrate);
1937  $this->line->localtax1_tx = $localtaxes_type[1];
1938  $this->line->localtax2_tx = $localtaxes_type[3];
1939  $this->line->localtax1_type = $localtaxes_type[0];
1940  $this->line->localtax2_type = $localtaxes_type[2];
1941 
1942  $this->line->total_ttc = $tmp[2];
1943  $this->line->total_ht = $tmp[0];
1944  $this->line->total_tva = $tmp[1];
1945  $this->line->total_localtax1 = $tmp[9];
1946  $this->line->total_localtax2 = $tmp[10];
1947 
1948  $this->line->fk_expensereport = $this->id;
1949  $this->line->qty = $qty;
1950  $this->line->date = $date;
1951  $this->line->fk_c_type_fees = $fk_c_type_fees;
1952  $this->line->fk_c_exp_tax_cat = $fk_c_exp_tax_cat;
1953  $this->line->comments = $comments;
1954  $this->line->fk_projet = $fk_project; // deprecated
1955  $this->line->fk_project = $fk_project;
1956 
1957  $this->line->fk_ecm_files = $fk_ecm_files;
1958 
1959  $this->applyOffset();
1960  $this->checkRules($type, $seller);
1961 
1962  $result = $this->line->insert(0, true);
1963  if ($result > 0) {
1964  $result = $this->update_price(1); // This method is designed to add line from user input so total calculation must be done using 'auto' mode.
1965  if ($result > 0) {
1966  $this->db->commit();
1967  return $this->line->id;
1968  } else {
1969  $this->db->rollback();
1970  return -1;
1971  }
1972  } else {
1973  $this->error = $this->line->error;
1974  dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
1975  $this->db->rollback();
1976  return -2;
1977  }
1978  } else {
1979  dol_syslog(get_class($this)."::addline status of expense report must be Draft to allow use of ->addline()", LOG_ERR);
1980  $this->error = 'ErrorExpenseNotDraft';
1981  return -3;
1982  }
1983  }
1984 
1992  public function checkRules($type = 0, $seller = '')
1993  {
1994  global $user, $conf, $db, $langs, $mysoc;
1995 
1996  $langs->load('trips');
1997 
1998  // We don't know seller and buyer for expense reports
1999  if (!is_object($seller)) {
2000  $seller = $mysoc; // We use same than current company (expense report are often done in same country)
2001  $seller->tva_assuj = 1; // Most seller uses vat
2002  }
2003 
2004  $expensereportrule = new ExpenseReportRule($db);
2005  $rulestocheck = $expensereportrule->getAllRule($this->line->fk_c_type_fees, $this->line->date, $this->fk_user_author);
2006 
2007  $violation = 0;
2008  $rule_warning_message_tab = array();
2009 
2010  $current_total_ttc = $this->line->total_ttc;
2011  $new_current_total_ttc = $this->line->total_ttc;
2012 
2013  // check if one is violated
2014  foreach ($rulestocheck as $rule) {
2015  if (in_array($rule->code_expense_rules_type, array('EX_DAY', 'EX_MON', 'EX_YEA'))) {
2016  $amount_to_test = $this->line->getExpAmount($rule, $this->fk_user_author, $rule->code_expense_rules_type);
2017  } else {
2018  $amount_to_test = $current_total_ttc; // EX_EXP
2019  }
2020 
2021  $amount_to_test = $amount_to_test - $current_total_ttc + $new_current_total_ttc; // if amount as been modified by a previous rule
2022 
2023  if ($amount_to_test > $rule->amount) {
2024  $violation++;
2025 
2026  if ($rule->restrictive) {
2027  $this->error = 'ExpenseReportConstraintViolationError';
2028  $this->errors[] = $this->error;
2029 
2030  $new_current_total_ttc -= $amount_to_test - $rule->amount; // ex, entered 16€, limit 12€, subtracts 4€;
2031  $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));
2032  } else {
2033  $this->error = 'ExpenseReportConstraintViolationWarning';
2034  $this->errors[] = $this->error;
2035 
2036  $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));
2037  }
2038 
2039  // No break, we sould test if another rule is violated
2040  }
2041  }
2042 
2043  $this->line->rule_warning_message = implode('\n', $rule_warning_message_tab);
2044 
2045  if ($violation > 0) {
2046  $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);
2047 
2048  $this->line->value_unit = $tmp[5];
2049  $this->line->total_ttc = $tmp[2];
2050  $this->line->total_ht = $tmp[0];
2051  $this->line->total_tva = $tmp[1];
2052  $this->line->total_localtax1 = $tmp[9];
2053  $this->line->total_localtax2 = $tmp[10];
2054 
2055  return false;
2056  } else {
2057  return true;
2058  }
2059  }
2060 
2068  public function applyOffset($type = 0, $seller = '')
2069  {
2070  global $conf, $mysoc;
2071 
2072  if (empty($conf->global->MAIN_USE_EXPENSE_IK)) {
2073  return false;
2074  }
2075 
2076  $userauthor = new User($this->db);
2077  if ($userauthor->fetch($this->fk_user_author) <= 0) {
2078  $this->error = 'ErrorCantFetchUser';
2079  $this->errors[] = 'ErrorCantFetchUser';
2080  return false;
2081  }
2082 
2083  // We don't know seller and buyer for expense reports
2084  if (!is_object($seller)) {
2085  $seller = $mysoc; // We use same than current company (expense report are often done in same country)
2086  $seller->tva_assuj = 1; // Most seller uses vat
2087  }
2088 
2089  $expenseik = new ExpenseReportIk($this->db);
2090  $range = $expenseik->getRangeByUser($userauthor, $this->line->fk_c_exp_tax_cat);
2091 
2092  if (empty($range)) {
2093  $this->error = 'ErrorNoRangeAvailable';
2094  $this->errors[] = 'ErrorNoRangeAvailable';
2095  return false;
2096  }
2097 
2098  if (!empty($conf->global->MAIN_EXPENSE_APPLY_ENTIRE_OFFSET)) {
2099  $ikoffset = $range->ikoffset;
2100  } else {
2101  $ikoffset = $range->ikoffset / 12; // The amount of offset is a global value for the year
2102  }
2103 
2104  // Test if ikoffset has been applied for the current month
2105  if (!$this->offsetAlreadyGiven()) {
2106  $new_up = $range->coef + ($ikoffset / $this->line->qty);
2107  $tmp = calcul_price_total($this->line->qty, $new_up, 0, $this->line->vatrate, 0, 0, 0, 'TTC', 0, $type, $seller);
2108 
2109  $this->line->value_unit = $tmp[5];
2110  $this->line->total_ttc = $tmp[2];
2111  $this->line->total_ht = $tmp[0];
2112  $this->line->total_tva = $tmp[1];
2113  $this->line->total_localtax1 = $tmp[9];
2114  $this->line->total_localtax2 = $tmp[10];
2115 
2116  return true;
2117  }
2118 
2119  return false;
2120  }
2121 
2127  public function offsetAlreadyGiven()
2128  {
2129  $sql = 'SELECT e.rowid FROM '.MAIN_DB_PREFIX.'expensereport e';
2130  $sql .= " INNER JOIN ".MAIN_DB_PREFIX."expensereport_det d ON (e.rowid = d.fk_expensereport)";
2131  $sql .= " INNER JOIN ".MAIN_DB_PREFIX."c_type_fees f ON (d.fk_c_type_fees = f.id AND f.code = 'EX_KME')";
2132  $sql .= " WHERE e.fk_user_author = ".(int) $this->fk_user_author;
2133  $sql .= " AND YEAR(d.date) = '".dol_print_date($this->line->date, '%Y')."' AND MONTH(d.date) = '".dol_print_date($this->line->date, '%m')."'";
2134  if (!empty($this->line->id)) {
2135  $sql .= ' AND d.rowid <> '.((int) $this->line->id);
2136  }
2137 
2138  dol_syslog(get_class($this)."::offsetAlreadyGiven");
2139  $resql = $this->db->query($sql);
2140  if ($resql) {
2141  $num = $this->db->num_rows($resql);
2142  if ($num > 0) {
2143  return true;
2144  }
2145  } else {
2146  dol_print_error($this->db);
2147  }
2148 
2149  return false;
2150  }
2151 
2169  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)
2170  {
2171  global $user, $mysoc;
2172 
2173  if ($this->status == self::STATUS_DRAFT || $this->status == self::STATUS_REFUSED) {
2174  $this->db->begin();
2175 
2176  $error = 0;
2177  $type = 0; // TODO What if type is service ?
2178 
2179  // We don't know seller and buyer for expense reports
2180  $seller = $mysoc; // We use same than current company (expense report are often done in same country)
2181  $seller->tva_assuj = 1; // Most seller uses vat
2182  $seller->localtax1_assuj = $mysoc->localtax1_assuj; // We don't know, we reuse the state of company
2183  $seller->localtax2_assuj = $mysoc->localtax1_assuj; // We don't know, we reuse the state of company
2184  $buyer = new Societe($this->db);
2185 
2186  $localtaxes_type = getLocalTaxesFromRate($vatrate, 0, $buyer, $seller);
2187 
2188  // Clean vat code
2189  $reg = array();
2190  $vat_src_code = '';
2191  if (preg_match('/\‍((.*)\‍)/', $vatrate, $reg)) {
2192  $vat_src_code = $reg[1];
2193  $vatrate = preg_replace('/\s*\‍(.*\‍)/', '', $vatrate); // Remove code into vatrate.
2194  }
2195  $vatrate = preg_replace('/\*/', '', $vatrate);
2196 
2197  $tmp = calcul_price_total($qty, $value_unit, 0, $vatrate, -1, -1, 0, 'TTC', 0, $type, $seller, $localtaxes_type);
2198  //var_dump($vatrate);var_dump($localtaxes_type);var_dump($tmp);exit;
2199  // calcul total of line
2200  //$total_ttc = price2num($qty*$value_unit, 'MT');
2201 
2202  $tx_tva = $vatrate / 100;
2203  $tx_tva = $tx_tva + 1;
2204 
2205  $this->line = new ExpenseReportLine($this->db);
2206  $this->line->comments = $comments;
2207  $this->line->qty = $qty;
2208  $this->line->value_unit = $value_unit;
2209  $this->line->date = $date;
2210 
2211  $this->line->fk_expensereport = $expensereport_id;
2212  $this->line->fk_c_type_fees = $type_fees_id;
2213  $this->line->fk_c_exp_tax_cat = $fk_c_exp_tax_cat;
2214  $this->line->fk_projet = $projet_id; // deprecated
2215  $this->line->fk_project = $projet_id;
2216 
2217  $this->line->vat_src_code = $vat_src_code;
2218  $this->line->vatrate = price2num($vatrate);
2219  $this->line->localtax1_tx = $localtaxes_type[1];
2220  $this->line->localtax2_tx = $localtaxes_type[3];
2221  $this->line->localtax1_type = $localtaxes_type[0];
2222  $this->line->localtax2_type = $localtaxes_type[2];
2223 
2224  $this->line->total_ttc = $tmp[2];
2225  $this->line->total_ht = $tmp[0];
2226  $this->line->total_tva = $tmp[1];
2227  $this->line->total_localtax1 = $tmp[9];
2228  $this->line->total_localtax2 = $tmp[10];
2229 
2230  $this->line->fk_ecm_files = $fk_ecm_files;
2231 
2232  $this->line->id = ((int) $rowid);
2233 
2234  // Select des infos sur le type fees
2235  $sql = "SELECT c.code as code_type_fees, c.label as libelle_type_fees";
2236  $sql .= " FROM ".MAIN_DB_PREFIX."c_type_fees as c";
2237  $sql .= " WHERE c.id = ".((int) $type_fees_id);
2238  $resql = $this->db->query($sql);
2239  if ($resql) {
2240  $objp_fees = $this->db->fetch_object($resql);
2241  $this->line->type_fees_code = $objp_fees->code_type_fees;
2242  $this->line->type_fees_libelle = $objp_fees->libelle_type_fees;
2243  $this->db->free($resql);
2244  }
2245 
2246  // Select des informations du projet
2247  $sql = "SELECT p.ref as ref_projet, p.title as title_projet";
2248  $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
2249  $sql .= " WHERE p.rowid = ".((int) $projet_id);
2250  $resql = $this->db->query($sql);
2251  if ($resql) {
2252  $objp_projet = $this->db->fetch_object($resql);
2253  $this->line->projet_ref = $objp_projet->ref_projet;
2254  $this->line->projet_title = $objp_projet->title_projet;
2255  $this->db->free($resql);
2256  }
2257 
2258  $this->applyOffset();
2259  $this->checkRules();
2260 
2261  $result = $this->line->update($user);
2262  if ($result < 0) {
2263  $error++;
2264  }
2265 
2266  if (!$error && !$notrigger) {
2267  // Call triggers
2268  $result = $this->call_trigger('EXPENSE_REPORT_DET_MODIFY', $user);
2269  if ($result < 0) {
2270  $error++;
2271  }
2272  // End call triggers
2273  }
2274 
2275  if (!$error) {
2276  $this->db->commit();
2277  return 1;
2278  } else {
2279  $this->error = $this->line->error;
2280  $this->errors = $this->line->errors;
2281  $this->db->rollback();
2282  return -2;
2283  }
2284  }
2285 
2286  return 0;
2287  }
2288 
2297  public function deleteline($rowid, $fuser = '', $notrigger = 0)
2298  {
2299  $error=0;
2300 
2301  $this->db->begin();
2302 
2303  if (!$notrigger) {
2304  // Call triggers
2305  $result = $this->call_trigger('EXPENSE_REPORT_DET_DELETE', $fuser);
2306  if ($result < 0) {
2307  $error++;
2308  }
2309  // End call triggers
2310  }
2311 
2312  $sql = ' DELETE FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2313  $sql .= ' WHERE rowid = '.((int) $rowid);
2314 
2315  dol_syslog(get_class($this)."::deleteline sql=".$sql);
2316  $result = $this->db->query($sql);
2317 
2318  if (!$result || $error > 0 ) {
2319  $this->error = $this->db->error();
2320  dol_syslog(get_class($this)."::deleteline Error ".$this->error, LOG_ERR);
2321  $this->db->rollback();
2322  return -1;
2323  }
2324 
2325  $this->update_price(1);
2326 
2327  $this->db->commit();
2328 
2329  return 1;
2330  }
2331 
2332  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2341  public function periode_existe($fuser, $date_debut, $date_fin)
2342  {
2343  global $conf;
2344 
2345  // phpcs:enable
2346  $sql = "SELECT rowid, date_debut, date_fin";
2347  $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element;
2348  $sql .= " WHERE entity = ".((int) $conf->entity); // not shared, only for the current entity
2349  $sql .= " AND fk_user_author = ".((int) $fuser->id);
2350 
2351  dol_syslog(get_class($this)."::periode_existe sql=".$sql);
2352  $result = $this->db->query($sql);
2353  if ($result) {
2354  $num_rows = $this->db->num_rows($result); $i = 0;
2355 
2356  if ($num_rows > 0) {
2357  $date_d_form = $date_debut;
2358  $date_f_form = $date_fin;
2359 
2360  while ($i < $num_rows) {
2361  $objp = $this->db->fetch_object($result);
2362 
2363  $date_d_req = $this->db->jdate($objp->date_debut); // 3
2364  $date_f_req = $this->db->jdate($objp->date_fin); // 4
2365 
2366  if (!($date_f_form < $date_d_req || $date_d_form > $date_f_req)) {
2367  return $objp->rowid;
2368  }
2369 
2370  $i++;
2371  }
2372 
2373  return 0;
2374  } else {
2375  return 0;
2376  }
2377  } else {
2378  $this->error = $this->db->lasterror();
2379  dol_syslog(get_class($this)."::periode_existe Error ".$this->error, LOG_ERR);
2380  return -1;
2381  }
2382  }
2383 
2384 
2385  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2393  {
2394  // phpcs:enable
2395  $users_validator = array();
2396 
2397  $sql = "SELECT DISTINCT ur.fk_user";
2398  $sql .= " FROM ".MAIN_DB_PREFIX."user_rights as ur, ".MAIN_DB_PREFIX."rights_def as rd";
2399  $sql .= " WHERE ur.fk_id = rd.id and rd.module = 'expensereport' AND rd.perms = 'approve'"; // Permission 'Approve';
2400  $sql .= " UNION";
2401  $sql .= " SELECT DISTINCT ugu.fk_user";
2402  $sql .= " FROM ".MAIN_DB_PREFIX."usergroup_user as ugu, ".MAIN_DB_PREFIX."usergroup_rights as ur, ".MAIN_DB_PREFIX."rights_def as rd";
2403  $sql .= " WHERE ugu.fk_usergroup = ur.fk_usergroup AND ur.fk_id = rd.id and rd.module = 'expensereport' AND rd.perms = 'approve'"; // Permission 'Approve';
2404  //print $sql;
2405 
2406  dol_syslog(get_class($this)."::fetch_users_approver_expensereport sql=".$sql);
2407  $result = $this->db->query($sql);
2408  if ($result) {
2409  $num_rows = $this->db->num_rows($result); $i = 0;
2410  while ($i < $num_rows) {
2411  $objp = $this->db->fetch_object($result);
2412  array_push($users_validator, $objp->fk_user);
2413  $i++;
2414  }
2415  return $users_validator;
2416  } else {
2417  $this->error = $this->db->lasterror();
2418  dol_syslog(get_class($this)."::fetch_users_approver_expensereport Error ".$this->error, LOG_ERR);
2419  return -1;
2420  }
2421  }
2422 
2434  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2435  {
2436  global $conf;
2437 
2438  $outputlangs->load("trips");
2439 
2440  if (!dol_strlen($modele)) {
2441  if (!empty($this->model_pdf)) {
2442  $modele = $this->model_pdf;
2443  } elseif (!empty($this->modelpdf)) { // deprecated
2444  $modele = $this->modelpdf;
2445  } elseif (!empty($conf->global->EXPENSEREPORT_ADDON_PDF)) {
2446  $modele = $conf->global->EXPENSEREPORT_ADDON_PDF;
2447  }
2448  }
2449 
2450  if (!empty($modele)) {
2451  $modelpath = "core/modules/expensereport/doc/";
2452 
2453  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2454  } else {
2455  return 0;
2456  }
2457  }
2458 
2465  public function listOfTypes($active = 1)
2466  {
2467  global $langs;
2468  $ret = array();
2469  $sql = "SELECT id, code, label";
2470  $sql .= " FROM ".MAIN_DB_PREFIX."c_type_fees";
2471  $sql .= " WHERE active = ".((int) $active);
2472  dol_syslog(get_class($this)."::listOfTypes", LOG_DEBUG);
2473  $result = $this->db->query($sql);
2474  if ($result) {
2475  $num = $this->db->num_rows($result);
2476  $i = 0;
2477  while ($i < $num) {
2478  $obj = $this->db->fetch_object($result);
2479  $ret[$obj->code] = (($langs->transnoentitiesnoconv($obj->code) != $obj->code) ? $langs->transnoentitiesnoconv($obj->code) : $obj->label);
2480  $i++;
2481  }
2482  } else {
2483  dol_print_error($this->db);
2484  }
2485  return $ret;
2486  }
2487 
2488  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2494  public function load_state_board()
2495  {
2496  // phpcs:enable
2497  global $conf, $user;
2498 
2499  $this->nb = array();
2500 
2501  $sql = "SELECT count(ex.rowid) as nb";
2502  $sql .= " FROM ".MAIN_DB_PREFIX."expensereport as ex";
2503  $sql .= " WHERE ex.fk_statut > 0";
2504  $sql .= " AND ex.entity IN (".getEntity('expensereport').")";
2505  if (empty($user->rights->expensereport->readall)) {
2506  $userchildids = $user->getAllChildIds(1);
2507  $sql .= " AND (ex.fk_user_author IN (".$this->db->sanitize(join(',', $userchildids)).")";
2508  $sql .= " OR ex.fk_user_validator IN (".$this->db->sanitize(join(',', $userchildids))."))";
2509  }
2510 
2511  $resql = $this->db->query($sql);
2512  if ($resql) {
2513  while ($obj = $this->db->fetch_object($resql)) {
2514  $this->nb["expensereports"] = $obj->nb;
2515  }
2516  $this->db->free($resql);
2517  return 1;
2518  } else {
2519  dol_print_error($this->db);
2520  $this->error = $this->db->error();
2521  return -1;
2522  }
2523  }
2524 
2525  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2533  public function load_board($user, $option = 'topay')
2534  {
2535  // phpcs:enable
2536  global $conf, $langs;
2537 
2538  if ($user->socid) {
2539  return -1; // protection pour eviter appel par utilisateur externe
2540  }
2541 
2542  $now = dol_now();
2543 
2544  $sql = "SELECT ex.rowid, ex.date_valid";
2545  $sql .= " FROM ".MAIN_DB_PREFIX."expensereport as ex";
2546  if ($option == 'toapprove') {
2547  $sql .= " WHERE ex.fk_statut = ".self::STATUS_VALIDATED;
2548  } else {
2549  $sql .= " WHERE ex.fk_statut = ".self::STATUS_APPROVED;
2550  }
2551  $sql .= " AND ex.entity IN (".getEntity('expensereport').")";
2552  if (empty($user->rights->expensereport->readall)) {
2553  $userchildids = $user->getAllChildIds(1);
2554  $sql .= " AND (ex.fk_user_author IN (".$this->db->sanitize(join(',', $userchildids)).")";
2555  $sql .= " OR ex.fk_user_validator IN (".$this->db->sanitize(join(',', $userchildids))."))";
2556  }
2557 
2558  $resql = $this->db->query($sql);
2559  if ($resql) {
2560  $langs->load("trips");
2561 
2562  $response = new WorkboardResponse();
2563  if ($option == 'toapprove') {
2564  $response->warning_delay = $conf->expensereport->approve->warning_delay / 60 / 60 / 24;
2565  $response->label = $langs->trans("ExpenseReportsToApprove");
2566  $response->labelShort = $langs->trans("ToApprove");
2567  $response->url = DOL_URL_ROOT.'/expensereport/list.php?mainmenu=hrm&amp;statut='.self::STATUS_VALIDATED;
2568  } else {
2569  $response->warning_delay = $conf->expensereport->payment->warning_delay / 60 / 60 / 24;
2570  $response->label = $langs->trans("ExpenseReportsToPay");
2571  $response->labelShort = $langs->trans("StatusToPay");
2572  $response->url = DOL_URL_ROOT.'/expensereport/list.php?mainmenu=hrm&amp;statut='.self::STATUS_APPROVED;
2573  }
2574  $response->img = img_object('', "trip");
2575 
2576  while ($obj = $this->db->fetch_object($resql)) {
2577  $response->nbtodo++;
2578 
2579  if ($option == 'toapprove') {
2580  if ($this->db->jdate($obj->date_valid) < ($now - $conf->expensereport->approve->warning_delay)) {
2581  $response->nbtodolate++;
2582  }
2583  } else {
2584  if ($this->db->jdate($obj->date_valid) < ($now - $conf->expensereport->payment->warning_delay)) {
2585  $response->nbtodolate++;
2586  }
2587  }
2588  }
2589 
2590  return $response;
2591  } else {
2592  dol_print_error($this->db);
2593  $this->error = $this->db->error();
2594  return -1;
2595  }
2596  }
2597 
2604  public function hasDelay($option)
2605  {
2606  global $conf;
2607 
2608  // Only valid expenses reports
2609  if ($option == 'toapprove' && $this->status != 2) {
2610  return false;
2611  }
2612  if ($option == 'topay' && $this->status != 5) {
2613  return false;
2614  }
2615 
2616  $now = dol_now();
2617  if ($option == 'toapprove') {
2618  return (!empty($this->datevalid) ? $this->datevalid : $this->date_valid) < ($now - $conf->expensereport->approve->warning_delay);
2619  } else {
2620  return (!empty($this->datevalid) ? $this->datevalid : $this->date_valid) < ($now - $conf->expensereport->payment->warning_delay);
2621  }
2622  }
2623 
2629  public function getVentilExportCompta()
2630  {
2631  $alreadydispatched = 0;
2632 
2633  $type = 'expense_report';
2634 
2635  $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);
2636  $resql = $this->db->query($sql);
2637  if ($resql) {
2638  $obj = $this->db->fetch_object($resql);
2639  if ($obj) {
2640  $alreadydispatched = $obj->nb;
2641  }
2642  } else {
2643  $this->error = $this->db->lasterror();
2644  return -1;
2645  }
2646 
2647  if ($alreadydispatched) {
2648  return 1;
2649  }
2650  return 0;
2651  }
2652 
2658  public function getSumPayments()
2659  {
2660  $table = 'payment_expensereport';
2661  $field = 'fk_expensereport';
2662 
2663  $sql = 'SELECT sum(amount) as amount';
2664  $sql .= ' FROM '.MAIN_DB_PREFIX.$table;
2665  $sql .= " WHERE ".$field." = ".((int) $this->id);
2666 
2667  dol_syslog(get_class($this)."::getSumPayments", LOG_DEBUG);
2668  $resql = $this->db->query($sql);
2669  if ($resql) {
2670  $obj = $this->db->fetch_object($resql);
2671  $this->db->free($resql);
2672  return (empty($obj->amount) ? 0 : $obj->amount);
2673  } else {
2674  $this->error = $this->db->lasterror();
2675  return -1;
2676  }
2677  }
2678 
2687  public function computeTotalKm($fk_cat, $qty, $tva)
2688  {
2689  global $langs, $db, $conf;
2690 
2691  $cumulYearQty = 0;
2692  $ranges = array();
2693  $coef = 0;
2694 
2695 
2696  if ($fk_cat < 0) {
2697  $this->error = $langs->trans('ErrorBadParameterCat');
2698  return -1;
2699  }
2700 
2701  if ($qty <= 0) {
2702  $this->error = $langs->trans('ErrorBadParameterQty');
2703  return -1;
2704  }
2705 
2706  $currentUser = new User($db);
2707  $currentUser->fetch($this->fk_user);
2708  $currentUser->getrights('expensereport');
2709  //Clean
2710  $qty = price2num($qty);
2711 
2712  $sql = " SELECT r.range_ik, t.ikoffset, t.coef";
2713  $sql .= " FROM ".MAIN_DB_PREFIX."expensereport_ik t";
2714  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_exp_tax_range r ON r.rowid = t.fk_range";
2715  $sql .= " WHERE t.fk_c_exp_tax_cat = ".(int) $fk_cat;
2716  $sql .= " ORDER BY r.range_ik ASC";
2717 
2718  dol_syslog("expenseReport::computeTotalkm sql=".$sql, LOG_DEBUG);
2719 
2720  $result = $this->db->query($sql);
2721 
2722  if ($result) {
2723  if ($conf->global->EXPENSEREPORT_CALCULATE_MILEAGE_EXPENSE_COEFFICIENT_ON_CURRENT_YEAR) {
2724  $arrayDate = dol_getdate(dol_now());
2725  $sql = " SELECT count(n.qty) as cumul FROM ".MAIN_DB_PREFIX."expensereport_det n";
2726  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."expensereport e ON e.rowid = n.fk_expensereport";
2727  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_type_fees tf ON tf.id = n.fk_c_type_fees";
2728  $sql.= " WHERE e.fk_user_author = ".(int) $this->fk_user_author;
2729  $sql.= " AND YEAR(n.date) = ".(int) $arrayDate['year'];
2730  $sql.= " AND tf.code = 'EX_KME' ";
2731  $sql.= " AND e.fk_statut = ".(int) ExpenseReport::STATUS_VALIDATED;
2732 
2733  $resql = $this->db->query($sql);
2734 
2735  if ($resql) {
2736  $obj = $this->db->fetch_object($resql);
2737  $cumulYearQty = $obj->cumul;
2738  }
2739 
2740  $qty = $cumulYearQty + $qty;
2741  }
2742 
2743  $num = $this->db->num_rows($result);
2744 
2745  if ($num) {
2746  for ($i = 0; $i < $num; $i++) {
2747  $obj = $this->db->fetch_object($result);
2748 
2749  $ranges[$i] = $obj;
2750  }
2751 
2752 
2753  for ($i = 0; $i < $num; $i++) {
2754  if ($i < ($num - 1)) {
2755  if ($qty > $ranges[$i]->range_ik && $qty < $ranges[$i+1]->range_ik) {
2756  $coef = $ranges[$i]->coef;
2757  $offset = $ranges[$i]->ikoffset;
2758  }
2759  } else {
2760  if ($qty > $ranges[$i]->range_ik) {
2761  $coef = $ranges[$i]->coef;
2762  $offset = $ranges[$i]->ikoffset;
2763  }
2764  }
2765  }
2766  $total_ht = $coef;
2767  return $total_ht;
2768  } else {
2769  $this->error = $langs->trans('TaxUndefinedForThisCategory');
2770  return 0;
2771  }
2772  } else {
2773  $this->error = $this->db->error()." sql=".$sql;
2774 
2775  return -1;
2776  }
2777  }
2778 
2786  public function getKanbanView($option = '', $arraydata = null)
2787  {
2788  global $langs;
2789 
2790  $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2791 
2792  $return = '<div class="box-flex-item box-flex-grow-zero">';
2793  $return .= '<div class="info-box info-box-sm">';
2794  $return .= '<span class="info-box-icon bg-infobox-action">';
2795  $return .= img_picto('', $this->picto);
2796  $return .= '</span>';
2797  $return .= '<div class="info-box-content">';
2798  $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref).'</span>';
2799  $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
2800  if (property_exists($this, 'fk_user_author') && !empty($this->id)) {
2801  $return .= '<br><span class="info-box-label">'.$this->fk_user_author.'</span>';
2802  }
2803  if (property_exists($this, 'date_debut') && property_exists($this, 'date_fin')) {
2804  $return .= '<br><span class="info-box-label">'.dol_print_date($this->date_debut, 'day').'</span>';
2805  $return .= ' <span class="opacitymedium">'.$langs->trans("To").'</span> ';
2806  $return .= '<span class="info-box-label">'.dol_print_date($this->date_fin, 'day').'</span>';
2807  }
2808  if (method_exists($this, 'getLibStatut')) {
2809  $return .= '<br><div class="info-box-status margintoponly">'.$this->getLibStatut(3).'</div>';
2810  }
2811  $return .= '</div>';
2812  $return .= '</div>';
2813  $return .= '</div>';
2814  return $return;
2815  }
2816 }
2817 
2818 
2823 {
2827  public $db;
2828 
2832  public $table_element = 'expensereport_det';
2833 
2837  public $error = '';
2838 
2842  public $rowid;
2843 
2844  public $comments;
2845  public $qty;
2846  public $value_unit;
2847  public $date;
2848 
2852  public $fk_c_type_fees;
2853 
2857  public $fk_c_exp_tax_cat;
2858 
2862  public $fk_projet;
2863 
2867  public $fk_expensereport;
2868 
2869  public $type_fees_code;
2870  public $type_fees_libelle;
2871  public $type_fees_accountancy_code;
2872 
2873  public $projet_ref;
2874  public $projet_title;
2875  public $rang;
2876 
2877  public $vatrate;
2878  public $vat_src_code;
2879  public $tva_tx;
2880  public $localtax1_tx;
2881  public $localtax2_tx;
2882  public $localtax1_type;
2883  public $localtax2_type;
2884 
2885  public $total_ht;
2886  public $total_tva;
2887  public $total_ttc;
2888  public $total_localtax1;
2889  public $total_localtax2;
2890 
2891  // Multicurrency
2895  public $fk_multicurrency;
2896 
2900  public $multicurrency_code;
2901  public $multicurrency_tx;
2902  public $multicurrency_total_ht;
2903  public $multicurrency_total_tva;
2904  public $multicurrency_total_ttc;
2905 
2909  public $fk_ecm_files;
2910 
2911  public $rule_warning_message;
2912 
2913 
2919  public function __construct($db)
2920  {
2921  $this->db = $db;
2922  }
2923 
2930  public function fetch($rowid)
2931  {
2932  $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,';
2933  $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,';
2934  $sql .= ' fde.localtax1_tx, fde.localtax2_tx, fde.localtax1_type, fde.localtax2_type, fde.total_localtax1, fde.total_localtax2, fde.rule_warning_message,';
2935  $sql .= ' ctf.code as type_fees_code, ctf.label as type_fees_libelle,';
2936  $sql .= ' pjt.rowid as projet_id, pjt.title as projet_title, pjt.ref as projet_ref';
2937  $sql .= ' FROM '.MAIN_DB_PREFIX.'expensereport_det as fde';
2938  $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.
2939  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'projet as pjt ON fde.fk_projet=pjt.rowid';
2940  $sql .= ' WHERE fde.rowid = '.((int) $rowid);
2941 
2942  $result = $this->db->query($sql);
2943 
2944  if ($result) {
2945  $objp = $this->db->fetch_object($result);
2946 
2947  $this->rowid = $objp->rowid;
2948  $this->id = $objp->rowid;
2949  $this->ref = $objp->ref;
2950  $this->fk_expensereport = $objp->fk_expensereport;
2951  $this->comments = $objp->comments;
2952  $this->qty = $objp->qty;
2953  $this->date = $objp->date;
2954  $this->dates = $this->db->jdate($objp->date);
2955  $this->value_unit = $objp->value_unit;
2956  $this->fk_c_type_fees = $objp->fk_c_type_fees;
2957  $this->fk_c_exp_tax_cat = $objp->fk_c_exp_tax_cat;
2958  $this->fk_projet = $objp->fk_project; // deprecated
2959  $this->fk_project = $objp->fk_project;
2960  $this->type_fees_code = $objp->type_fees_code;
2961  $this->type_fees_libelle = $objp->type_fees_libelle;
2962  $this->projet_ref = $objp->projet_ref;
2963  $this->projet_title = $objp->projet_title;
2964 
2965  $this->vatrate = $objp->vatrate;
2966  $this->vat_src_code = $objp->vat_src_code;
2967  $this->localtax1_tx = $objp->localtax1_tx;
2968  $this->localtax2_tx = $objp->localtax2_tx;
2969  $this->localtax1_type = $objp->localtax1_type;
2970  $this->localtax2_type = $objp->localtax2_type;
2971 
2972  $this->total_ht = $objp->total_ht;
2973  $this->total_tva = $objp->total_tva;
2974  $this->total_ttc = $objp->total_ttc;
2975  $this->total_localtax1 = $objp->total_localtax1;
2976  $this->total_localtax2 = $objp->total_localtax2;
2977 
2978  $this->fk_ecm_files = $objp->fk_ecm_files;
2979 
2980  $this->rule_warning_message = $objp->rule_warning_message;
2981 
2982  $this->db->free($result);
2983 
2984  return $this->id;
2985  } else {
2986  dol_print_error($this->db);
2987  return -1;
2988  }
2989  }
2990 
2998  public function insert($notrigger = 0, $fromaddline = false)
2999  {
3000  global $user, $conf;
3001 
3002  $error = 0;
3003 
3004  dol_syslog("ExpenseReportLine::Insert", LOG_DEBUG);
3005 
3006  // Clean parameters
3007  $this->comments = trim($this->comments);
3008  if (empty($this->value_unit)) {
3009  $this->value_unit = 0;
3010  }
3011  $this->qty = price2num($this->qty);
3012  $this->vatrate = price2num($this->vatrate);
3013  if (empty($this->fk_c_exp_tax_cat)) {
3014  $this->fk_c_exp_tax_cat = 0;
3015  }
3016 
3017  $this->db->begin();
3018 
3019  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'expensereport_det';
3020  $sql .= ' (fk_expensereport, fk_c_type_fees, fk_projet,';
3021  $sql .= ' tva_tx, vat_src_code,';
3022  $sql .= ' localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3023  $sql .= ' comments, qty, value_unit,';
3024  $sql .= ' total_ht, total_tva, total_ttc,';
3025  $sql .= ' total_localtax1, total_localtax2,';
3026  $sql .= ' date, rule_warning_message, fk_c_exp_tax_cat, fk_ecm_files)';
3027  $sql .= " VALUES (".$this->db->escape($this->fk_expensereport).",";
3028  $sql .= " ".((int) $this->fk_c_type_fees).",";
3029  $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')).",";
3030  $sql .= " ".((float) $this->vatrate).",";
3031  $sql .= " '".$this->db->escape(empty($this->vat_src_code) ? '' : $this->vat_src_code)."',";
3032  $sql .= " ".((float) price2num($this->localtax1_tx)).",";
3033  $sql .= " ".((float) price2num($this->localtax2_tx)).",";
3034  $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3035  $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3036  $sql .= " '".$this->db->escape($this->comments)."',";
3037  $sql .= " ".((float) $this->qty).",";
3038  $sql .= " ".((float) $this->value_unit).",";
3039  $sql .= " ".((float) price2num($this->total_ht)).",";
3040  $sql .= " ".((float) price2num($this->total_tva)).",";
3041  $sql .= " ".((float) price2num($this->total_ttc)).",";
3042  $sql .= " ".((float) price2num($this->total_localtax1)).",";
3043  $sql .= " ".((float) price2num($this->total_localtax2)).",";
3044  $sql .= " '".$this->db->idate($this->date)."',";
3045  $sql .= " ".(empty($this->rule_warning_message) ? 'null' : "'".$this->db->escape($this->rule_warning_message)."'").",";
3046  $sql .= " ".((int) $this->fk_c_exp_tax_cat).",";
3047  $sql .= " ".($this->fk_ecm_files > 0 ? ((int) $this->fk_ecm_files) : 'null');
3048  $sql .= ")";
3049 
3050  $resql = $this->db->query($sql);
3051  if ($resql) {
3052  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'expensereport_det');
3053 
3054 
3055  if (!$error && !$notrigger) {
3056  // Call triggers
3057  $result = $this->call_trigger('EXPENSE_REPORT_DET_CREATE', $user);
3058  if ($result < 0) {
3059  $error++;
3060  }
3061  // End call triggers
3062  }
3063 
3064 
3065  if (!$fromaddline) {
3066  $tmpparent = new ExpenseReport($this->db);
3067  $tmpparent->fetch($this->fk_expensereport);
3068  $result = $tmpparent->update_price(1);
3069  if ($result < 0) {
3070  $error++;
3071  $this->error = $tmpparent->error;
3072  $this->errors = $tmpparent->errors;
3073  }
3074  }
3075  } else {
3076  $error++;
3077  }
3078 
3079  if (!$error) {
3080  $this->db->commit();
3081  return $this->id;
3082  } else {
3083  $this->error = $this->db->lasterror();
3084  dol_syslog("ExpenseReportLine::insert Error ".$this->error, LOG_ERR);
3085  $this->db->rollback();
3086  return -2;
3087  }
3088  }
3089 
3098  public function getExpAmount(ExpenseReportRule $rule, $fk_user, $mode = 'day')
3099  {
3100  $amount = 0;
3101 
3102  $sql = 'SELECT SUM(d.total_ttc) as total_amount';
3103  $sql .= ' FROM '.MAIN_DB_PREFIX.'expensereport_det d';
3104  $sql .= ' INNER JOIN '.MAIN_DB_PREFIX.'expensereport e ON (d.fk_expensereport = e.rowid)';
3105  $sql .= ' WHERE e.fk_user_author = '.((int) $fk_user);
3106  if (!empty($this->id)) {
3107  $sql .= ' AND d.rowid <> '.((int) $this->id);
3108  }
3109  $sql .= ' AND d.fk_c_type_fees = '.((int) $rule->fk_c_type_fees);
3110  if ($mode == 'day' || $mode == 'EX_DAY') {
3111  $sql .= " AND d.date = '".dol_print_date($this->date, '%Y-%m-%d')."'";
3112  } elseif ($mode == 'mon' || $mode == 'EX_MON') {
3113  $sql .= " AND DATE_FORMAT(d.date, '%Y-%m') = '".dol_print_date($this->date, '%Y-%m')."'"; // @todo DATE_FORMAT is forbidden
3114  } elseif ($mode == 'year' || $mode == 'EX_YEA') {
3115  $sql .= " AND DATE_FORMAT(d.date, '%Y') = '".dol_print_date($this->date, '%Y')."'"; // @todo DATE_FORMAT is forbidden
3116  }
3117 
3118  dol_syslog('ExpenseReportLine::getExpAmount');
3119 
3120  $resql = $this->db->query($sql);
3121  if ($resql) {
3122  $num = $this->db->num_rows($resql);
3123  if ($num > 0) {
3124  $obj = $this->db->fetch_object($resql);
3125  $amount = (double) $obj->total_amount;
3126  }
3127  } else {
3128  dol_print_error($this->db);
3129  }
3130 
3131  return $amount + $this->total_ttc;
3132  }
3133 
3140  public function update(User $user)
3141  {
3142  global $langs, $conf;
3143 
3144  $error = 0;
3145 
3146  // Clean parameters
3147  $this->comments = trim($this->comments);
3148  $this->vatrate = price2num($this->vatrate);
3149  $this->value_unit = price2num($this->value_unit);
3150  if (empty($this->fk_c_exp_tax_cat)) {
3151  $this->fk_c_exp_tax_cat = 0;
3152  }
3153 
3154  $this->db->begin();
3155 
3156  // Update line in database
3157  $sql = "UPDATE ".MAIN_DB_PREFIX."expensereport_det SET";
3158  $sql .= " comments='".$this->db->escape($this->comments)."'";
3159  $sql .= ", value_unit = ".((float) $this->value_unit);
3160  $sql .= ", qty=".((float) $this->qty);
3161  $sql .= ", date='".$this->db->idate($this->date)."'";
3162  $sql .= ", total_ht=".((float) price2num($this->total_ht, 'MT'));
3163  $sql .= ", total_tva=".((float) price2num($this->total_tva, 'MT'));
3164  $sql .= ", total_ttc=".((float) price2num($this->total_ttc, 'MT'));
3165  $sql .= ", total_localtax1=".((float) price2num($this->total_localtax1, 'MT'));
3166  $sql .= ", total_localtax2=".((float) price2num($this->total_localtax2, 'MT'));
3167  $sql .= ", tva_tx=".((float) $this->vatrate);
3168  $sql .= ", vat_src_code='".$this->db->escape($this->vat_src_code)."'";
3169  $sql .= ", localtax1_tx=".((float) $this->localtax1_tx);
3170  $sql .= ", localtax2_tx=".((float) $this->localtax2_tx);
3171  $sql .= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'";
3172  $sql .= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'";
3173  $sql .= ", rule_warning_message='".$this->db->escape($this->rule_warning_message)."'";
3174  $sql .= ", fk_c_exp_tax_cat=".$this->db->escape($this->fk_c_exp_tax_cat);
3175  $sql .= ", fk_ecm_files=".($this->fk_ecm_files > 0 ? ((int) $this->fk_ecm_files) : 'null');
3176  if ($this->fk_c_type_fees) {
3177  $sql .= ", fk_c_type_fees = ".((int) $this->fk_c_type_fees);
3178  } else {
3179  $sql .= ", fk_c_type_fees=null";
3180  }
3181  if ($this->fk_project > 0) {
3182  $sql .= ", fk_projet=".((int) $this->fk_project);
3183  } else {
3184  $sql .= ", fk_projet=null";
3185  }
3186  $sql .= " WHERE rowid = ".((int) ($this->rowid ? $this->rowid : $this->id));
3187 
3188  dol_syslog("ExpenseReportLine::update");
3189 
3190  $resql = $this->db->query($sql);
3191  if ($resql) {
3192  $tmpparent = new ExpenseReport($this->db);
3193  $result = $tmpparent->fetch($this->fk_expensereport);
3194  if ($result > 0) {
3195  $result = $tmpparent->update_price(1);
3196  if ($result < 0) {
3197  $error++;
3198  $this->error = $tmpparent->error;
3199  $this->errors = $tmpparent->errors;
3200  }
3201  } else {
3202  $error++;
3203  $this->error = $tmpparent->error;
3204  $this->errors = $tmpparent->errors;
3205  }
3206  } else {
3207  $error++;
3208  dol_print_error($this->db);
3209  }
3210 
3211  if (!$error) {
3212  $this->db->commit();
3213  return 1;
3214  } else {
3215  $this->error = $this->db->lasterror();
3216  dol_syslog("ExpenseReportLine::update Error ".$this->error, LOG_ERR);
3217  $this->db->rollback();
3218  return -2;
3219  }
3220  }
3221 
3222  // ajouter ici comput_ ...
3223 }
$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:1507
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:1356
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:1559
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