dolibarr  17.0.4
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-2021 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 
63  public $lines = array();
64 
65  public $date_debut;
66 
67  public $date_fin;
68 
74  public $status;
75 
82  public $fk_statut;
83 
84  public $fk_c_paiement;
85  public $paid;
86 
87  public $user_author_infos;
88  public $user_validator_infos;
89 
90  public $rule_warning_message;
91 
92  // ACTIONS
93 
94  // Create
95  public $date_create;
96  public $fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
97 
98  // Update
99  public $date_modif;
100  public $fk_user_modif;
101 
102  // Refus
103  public $date_refuse;
104  public $detail_refuse;
105  public $fk_user_refuse;
106 
107  // Annulation
108  public $date_cancel;
109  public $detail_cancel;
110  public $fk_user_cancel;
111 
112  public $fk_user_validator; // User that is defined to approve
113 
114  // Validation
115  /* @deprecated */
116  public $datevalid;
117 
118  public $date_valid; // User making validation
119  public $fk_user_valid;
120  public $user_valid_infos;
121 
122  // Approve
123  public $date_approve;
124  public $fk_user_approve; // User that has approved
125 
126  // Paiement
127  public $user_paid_infos;
128 
129  public $localtax1; // for backward compatibility (real field should be total_localtax1 defined into CommonObject)
130  public $localtax2; // for backward compatibility (real field should be total_localtax2 defined into CommonObject)
131 
132  public $statuts = array();
133  public $statuts_short = array();
134  public $statuts_logo;
135 
136  // Multicurrency
140  public $fk_multicurrency;
141 
145  public $multicurrency_code;
146  public $multicurrency_tx;
147  public $multicurrency_total_ht;
148  public $multicurrency_total_tva;
149  public $multicurrency_total_ttc;
150 
151 
155  const STATUS_DRAFT = 0;
156 
160  const STATUS_VALIDATED = 2;
161 
165  const STATUS_CANCELED = 4;
166 
170  const STATUS_APPROVED = 5;
171 
175  const STATUS_REFUSED = 99;
176 
180  const STATUS_CLOSED = 6;
181 
182 
183  public $fields = array(
184  'rowid' =>array('type'=>'integer', 'label'=>'ID', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>10),
185  'ref' =>array('type'=>'varchar(50)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'showoncombobox'=>1, 'position'=>15),
186  'entity' =>array('type'=>'integer', 'label'=>'Entity', 'default'=>1, 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>20),
187  'ref_number_int' =>array('type'=>'integer', 'label'=>'Ref number int', 'enabled'=>1, 'visible'=>-1, 'position'=>25),
188  'ref_ext' =>array('type'=>'integer', 'label'=>'Ref ext', 'enabled'=>1, 'visible'=>-1, 'position'=>30),
189  'total_ht' =>array('type'=>'double(24,8)', 'label'=>'Total ht', 'enabled'=>1, 'visible'=>-1, 'position'=>35),
190  'total_tva' =>array('type'=>'double(24,8)', 'label'=>'Total tva', 'enabled'=>1, 'visible'=>-1, 'position'=>40),
191  'localtax1' =>array('type'=>'double(24,8)', 'label'=>'Localtax1', 'enabled'=>1, 'visible'=>-1, 'position'=>45),
192  'localtax2' =>array('type'=>'double(24,8)', 'label'=>'Localtax2', 'enabled'=>1, 'visible'=>-1, 'position'=>50),
193  'total_ttc' =>array('type'=>'double(24,8)', 'label'=>'Total ttc', 'enabled'=>1, 'visible'=>-1, 'position'=>55),
194  'date_debut' =>array('type'=>'date', 'label'=>'Date debut', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>60),
195  'date_fin' =>array('type'=>'date', 'label'=>'Date fin', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>65),
196  'date_valid' =>array('type'=>'datetime', 'label'=>'Date valid', 'enabled'=>1, 'visible'=>-1, 'position'=>75),
197  'date_approve' =>array('type'=>'datetime', 'label'=>'Date approve', 'enabled'=>1, 'visible'=>-1, 'position'=>80),
198  'date_refuse' =>array('type'=>'datetime', 'label'=>'Date refuse', 'enabled'=>1, 'visible'=>-1, 'position'=>85),
199  'date_cancel' =>array('type'=>'datetime', 'label'=>'Date cancel', 'enabled'=>1, 'visible'=>-1, 'position'=>90),
200  'fk_user_author' =>array('type'=>'integer', 'label'=>'Fk user author', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>100),
201  'fk_user_modif' =>array('type'=>'integer', 'label'=>'Fk user modif', 'enabled'=>1, 'visible'=>-1, 'position'=>105),
202  'fk_user_valid' =>array('type'=>'integer', 'label'=>'Fk user valid', 'enabled'=>1, 'visible'=>-1, 'position'=>110),
203  'fk_user_validator' =>array('type'=>'integer', 'label'=>'Fk user validator', 'enabled'=>1, 'visible'=>-1, 'position'=>115),
204  'fk_user_approve' =>array('type'=>'integer', 'label'=>'Fk user approve', 'enabled'=>1, 'visible'=>-1, 'position'=>120),
205  'fk_user_refuse' =>array('type'=>'integer', 'label'=>'Fk user refuse', 'enabled'=>1, 'visible'=>-1, 'position'=>125),
206  'fk_user_cancel' =>array('type'=>'integer', 'label'=>'Fk user cancel', 'enabled'=>1, 'visible'=>-1, 'position'=>130),
207  'fk_c_paiement' =>array('type'=>'integer', 'label'=>'Fk c paiement', 'enabled'=>1, 'visible'=>-1, 'position'=>140),
208  'paid' =>array('type'=>'integer', 'label'=>'Paid', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>145),
209  'note_public' =>array('type'=>'text', 'label'=>'Note public', 'enabled'=>1, 'visible'=>0, 'position'=>150),
210  'note_private' =>array('type'=>'text', 'label'=>'Note private', 'enabled'=>1, 'visible'=>0, 'position'=>155),
211  'detail_refuse' =>array('type'=>'varchar(255)', 'label'=>'Detail refuse', 'enabled'=>1, 'visible'=>-1, 'position'=>160),
212  'detail_cancel' =>array('type'=>'varchar(255)', 'label'=>'Detail cancel', 'enabled'=>1, 'visible'=>-1, 'position'=>165),
213  'integration_compta' =>array('type'=>'integer', 'label'=>'Integration compta', 'enabled'=>1, 'visible'=>-1, 'position'=>170),
214  'fk_bank_account' =>array('type'=>'integer', 'label'=>'Fk bank account', 'enabled'=>1, 'visible'=>-1, 'position'=>175),
215  'fk_multicurrency' =>array('type'=>'integer', 'label'=>'Fk multicurrency', 'enabled'=>1, 'visible'=>-1, 'position'=>185),
216  'multicurrency_code' =>array('type'=>'varchar(255)', 'label'=>'Multicurrency code', 'enabled'=>1, 'visible'=>-1, 'position'=>190),
217  'multicurrency_tx' =>array('type'=>'double(24,8)', 'label'=>'Multicurrency tx', 'enabled'=>1, 'visible'=>-1, 'position'=>195),
218  'multicurrency_total_ht' =>array('type'=>'double(24,8)', 'label'=>'Multicurrency total ht', 'enabled'=>1, 'visible'=>-1, 'position'=>200),
219  'multicurrency_total_tva' =>array('type'=>'double(24,8)', 'label'=>'Multicurrency total tva', 'enabled'=>1, 'visible'=>-1, 'position'=>205),
220  'multicurrency_total_ttc' =>array('type'=>'double(24,8)', 'label'=>'Multicurrency total ttc', 'enabled'=>1, 'visible'=>-1, 'position'=>210),
221  'extraparams' =>array('type'=>'varchar(255)', 'label'=>'Extraparams', 'enabled'=>1, 'visible'=>-1, 'position'=>220),
222  'date_create' =>array('type'=>'datetime', 'label'=>'Date create', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>300),
223  'tms' =>array('type'=>'timestamp', 'label'=>'Tms', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>305),
224  'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-1, 'position'=>1000),
225  'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'Model pdf', 'enabled'=>1, 'visible'=>0, 'position'=>1010),
226  'fk_statut' =>array('type'=>'integer', 'label'=>'Fk statut', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>500),
227  );
228 
234  public function __construct($db)
235  {
236  $this->db = $db;
237  $this->total_ht = 0;
238  $this->total_ttc = 0;
239  $this->total_tva = 0;
240  $this->total_localtax1 = 0;
241  $this->total_localtax2 = 0;
242  $this->localtax1 = 0; // For backward compatibility
243  $this->localtax2 = 0; // For backward compatibility
244  $this->modepaymentid = 0;
245 
246  // List of language codes for status
247  $this->statuts_short = array(0 => 'Draft', 2 => 'Validated', 4 => 'Canceled', 5 => 'Approved', 6 => 'Paid', 99 => 'Refused');
248  $this->statuts = array(0 => 'Draft', 2 => 'ValidatedWaitingApproval', 4 => 'Canceled', 5 => 'Approved', 6 => 'Paid', 99 => 'Refused');
249  $this->statuts_logo = array(0 => 'status0', 2 => 'status1', 4 => 'status6', 5 => 'status4', 6 => 'status6', 99 => 'status5');
250  }
251 
259  public function create($user, $notrigger = 0)
260  {
261  global $conf, $langs;
262 
263  $now = dol_now();
264 
265  $error = 0;
266 
267  // Check parameters
268  if (empty($this->date_debut) || empty($this->date_fin)) {
269  $this->error = $langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Date'));
270  return -1;
271  }
272 
273  $fuserid = $this->fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
274  if (empty($fuserid)) {
275  $fuserid = $user->id;
276  }
277 
278  $this->db->begin();
279 
280  $sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
281  $sql .= "ref";
282  $sql .= ",total_ht";
283  $sql .= ",total_ttc";
284  $sql .= ",total_tva";
285  $sql .= ",date_debut";
286  $sql .= ",date_fin";
287  $sql .= ",date_create";
288  $sql .= ",fk_user_creat";
289  $sql .= ",fk_user_author";
290  $sql .= ",fk_user_validator";
291  $sql .= ",fk_user_approve";
292  $sql .= ",fk_user_modif";
293  $sql .= ",fk_statut";
294  $sql .= ",fk_c_paiement";
295  $sql .= ",paid";
296  $sql .= ",note_public";
297  $sql .= ",note_private";
298  $sql .= ",entity";
299  $sql .= ") VALUES(";
300  $sql .= "'(PROV)'";
301  $sql .= ", ".price2num($this->total_ht, 'MT');
302  $sql .= ", ".price2num($this->total_ttc, 'MT');
303  $sql .= ", ".price2num($this->total_tva, 'MT');
304  $sql .= ", '".$this->db->idate($this->date_debut)."'";
305  $sql .= ", '".$this->db->idate($this->date_fin)."'";
306  $sql .= ", '".$this->db->idate($now)."'";
307  $sql .= ", ".((int) $user->id);
308  $sql .= ", ".((int) $fuserid);
309  $sql .= ", ".($this->fk_user_validator > 0 ? ((int) $this->fk_user_validator) : "null");
310  $sql .= ", ".($this->fk_user_approve > 0 ? ((int) $this->fk_user_approve) : "null");
311  $sql .= ", ".($this->fk_user_modif > 0 ? ((int) $this->fk_user_modif) : "null");
312  $sql .= ", ".($this->fk_statut > 1 ? ((int) $this->fk_statut) : 0);
313  $sql .= ", ".($this->modepaymentid ? ((int) $this->modepaymentid) : "null");
314  $sql .= ", 0";
315  $sql .= ", ".($this->note_public ? "'".$this->db->escape($this->note_public)."'" : "null");
316  $sql .= ", ".($this->note_private ? "'".$this->db->escape($this->note_private)."'" : "null");
317  $sql .= ", ".((int) $conf->entity);
318  $sql .= ")";
319 
320  $result = $this->db->query($sql);
321  if ($result) {
322  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
323  $this->ref = '(PROV'.$this->id.')';
324 
325  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element." SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".((int) $this->id);
326  $resql = $this->db->query($sql);
327  if (!$resql) {
328  $this->error = $this->db->lasterror();
329  $error++;
330  }
331 
332  if (!$error) {
333  if (is_array($this->lines) && count($this->lines) > 0) {
334  foreach ($this->lines as $line) {
335  // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
336  //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
337  if (!is_object($line)) {
338  $line = (object) $line;
339  $newndfline = new ExpenseReportLine($this->db);
340  $newndfline->fk_expensereport = $line->fk_expensereport;
341  $newndfline->fk_c_type_fees = $line->fk_c_type_fees;
342  $newndfline->fk_project = $line->fk_project;
343  $newndfline->vatrate = $line->vatrate;
344  $newndfline->vat_src_code = $line->vat_src_code;
345  $newndfline->localtax1_tx = $line->localtax1_tx;
346  $newndfline->localtax2_tx = $line->localtax2_tx;
347  $newndfline->localtax1_type = $line->localtax1_type;
348  $newndfline->localtax2_type = $line->localtax2_type;
349  $newndfline->comments = $line->comments;
350  $newndfline->qty = $line->qty;
351  $newndfline->value_unit = $line->value_unit;
352  $newndfline->total_ht = $line->total_ht;
353  $newndfline->total_ttc = $line->total_ttc;
354  $newndfline->total_tva = $line->total_tva;
355  $newndfline->total_localtax1 = $line->total_localtax1;
356  $newndfline->total_localtax2 = $line->total_localtax2;
357  $newndfline->date = $line->date;
358  $newndfline->rule_warning_message = $line->rule_warning_message;
359  $newndfline->fk_c_exp_tax_cat = $line->fk_c_exp_tax_cat;
360  $newndfline->fk_ecm_files = $line->fk_ecm_files;
361  } else {
362  $newndfline = $line;
363  }
364  //$newndfline=new ExpenseReportLine($this->db);
365  $newndfline->fk_expensereport = $this->id;
366  $result = $newndfline->insert();
367  if ($result < 0) {
368  $this->error = $newndfline->error;
369  $this->errors = $newndfline->errors;
370  $error++;
371  break;
372  }
373  }
374  }
375  }
376 
377  if (!$error) {
378  $result = $this->insertExtraFields();
379  if ($result < 0) {
380  $error++;
381  }
382  }
383 
384  if (!$error) {
385  $result = $this->update_price(1);
386  if ($result > 0) {
387  if (!$notrigger) {
388  // Call trigger
389  $result = $this->call_trigger('EXPENSE_REPORT_CREATE', $user);
390 
391  if ($result < 0) {
392  $error++;
393  }
394  // End call triggers
395  }
396 
397  if (empty($error)) {
398  $this->db->commit();
399  return $this->id;
400  } else {
401  $this->db->rollback();
402  return -4;
403  }
404  } else {
405  $this->db->rollback();
406  return -3;
407  }
408  } else {
409  dol_syslog(get_class($this)."::create error ".$this->error, LOG_ERR);
410  $this->db->rollback();
411  return -2;
412  }
413  } else {
414  $this->error = $this->db->lasterror()." sql=".$sql;
415  $this->db->rollback();
416  return -1;
417  }
418  }
419 
427  public function createFromClone(User $user, $fk_user_author)
428  {
429  global $hookmanager;
430 
431  $error = 0;
432 
433  if (empty($fk_user_author)) {
434  $fk_user_author = $user->id;
435  }
436 
437  $this->db->begin();
438 
439  // get extrafields so they will be clone
440  //foreach($this->lines as $line)
441  //$line->fetch_optionals();
442 
443  // Load source object
444  $objFrom = clone $this;
445 
446  $this->id = 0;
447  $this->ref = '';
448  $this->status = 0;
449  $this->fk_statut = 0; // deprecated
450 
451  // Clear fields
452  $this->fk_user_creat = $user->id;
453  $this->fk_user_author = $fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
454  $this->fk_user_valid = '';
455  $this->date_create = '';
456  $this->date_creation = '';
457  $this->date_validation = '';
458 
459  // Remove link on lines to a joined file
460  if (is_array($this->lines) && count($this->lines) > 0) {
461  foreach ($this->lines as $key => $line) {
462  $this->lines[$key]->fk_ecm_files = 0;
463  }
464  }
465 
466  // Create clone
467  $this->context['createfromclone'] = 'createfromclone';
468  $result = $this->create($user);
469  if ($result < 0) {
470  $error++;
471  }
472 
473  if (!$error) {
474  // Hook of thirdparty module
475  if (is_object($hookmanager)) {
476  $parameters = array('objFrom'=>$objFrom);
477  $action = '';
478  $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
479  if ($reshook < 0) {
480  $this->errors += $hookmanager->errors;
481  $this->error = $hookmanager->error;
482  $error++;
483  }
484  }
485  }
486 
487  unset($this->context['createfromclone']);
488 
489  // End
490  if (!$error) {
491  $this->db->commit();
492  return $this->id;
493  } else {
494  $this->db->rollback();
495  return -1;
496  }
497  }
498 
499 
508  public function update($user, $notrigger = 0, $userofexpensereport = null)
509  {
510  global $langs;
511 
512  $error = 0;
513  $this->db->begin();
514 
515  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
516  $sql .= " total_ht = ".$this->total_ht;
517  $sql .= " , total_ttc = ".$this->total_ttc;
518  $sql .= " , total_tva = ".$this->total_tva;
519  $sql .= " , date_debut = '".$this->db->idate($this->date_debut)."'";
520  $sql .= " , date_fin = '".$this->db->idate($this->date_fin)."'";
521  if ($userofexpensereport && is_object($userofexpensereport)) {
522  $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.
523  }
524  $sql .= " , fk_user_validator = ".($this->fk_user_validator > 0 ? $this->fk_user_validator : "null");
525  $sql .= " , fk_user_valid = ".($this->fk_user_valid > 0 ? $this->fk_user_valid : "null");
526  $sql .= " , fk_user_approve = ".($this->fk_user_approve > 0 ? $this->fk_user_approve : "null");
527  $sql .= " , fk_user_modif = ".$user->id;
528  $sql .= " , fk_statut = ".($this->fk_statut >= 0 ? $this->fk_statut : '0');
529  $sql .= " , fk_c_paiement = ".($this->fk_c_paiement > 0 ? $this->fk_c_paiement : "null");
530  $sql .= " , note_public = ".(!empty($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "''");
531  $sql .= " , note_private = ".(!empty($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "''");
532  $sql .= " , detail_refuse = ".(!empty($this->detail_refuse) ? "'".$this->db->escape($this->detail_refuse)."'" : "''");
533  $sql .= " WHERE rowid = ".((int) $this->id);
534 
535  dol_syslog(get_class($this)."::update", LOG_DEBUG);
536  $result = $this->db->query($sql);
537  if ($result) {
538  if (!$notrigger) {
539  // Call trigger
540  $result = $this->call_trigger('EXPENSE_REPORT_MODIFY', $user);
541 
542  if ($result < 0) {
543  $error++;
544  }
545  // End call triggers
546  }
547 
548  if (empty($error)) {
549  $this->db->commit();
550  return 1;
551  } else {
552  $this->db->rollback();
553  $this->error = $this->db->error();
554  return -2;
555  }
556  } else {
557  $this->db->rollback();
558  $this->error = $this->db->error();
559  return -1;
560  }
561  }
562 
570  public function fetch($id, $ref = '')
571  {
572  global $conf;
573 
574  $sql = "SELECT d.rowid, d.entity, d.ref, d.note_public, d.note_private,"; // DEFAULT
575  $sql .= " d.detail_refuse, d.detail_cancel, d.fk_user_refuse, d.fk_user_cancel,"; // ACTIONS
576  $sql .= " d.date_refuse, d.date_cancel,"; // ACTIONS
577  $sql .= " d.total_ht, d.total_ttc, d.total_tva,";
578  $sql .= " d.localtax1 as total_localtax1, d.localtax2 as total_localtax2,";
579  $sql .= " d.date_debut, d.date_fin, d.date_create, d.tms as date_modif, d.date_valid, d.date_approve,"; // DATES (datetime)
580  $sql .= " d.fk_user_creat, d.fk_user_author, d.fk_user_modif, d.fk_user_validator,";
581  $sql .= " d.fk_user_valid, d.fk_user_approve,";
582  $sql .= " d.fk_statut as status, d.fk_c_paiement, d.paid";
583  $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as d";
584  if ($ref) {
585  $sql .= " WHERE d.ref = '".$this->db->escape($ref)."'";
586  } else {
587  $sql .= " WHERE d.rowid = ".((int) $id);
588  }
589  //$sql.= $restrict;
590 
591  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
592  $resql = $this->db->query($sql);
593  if ($resql) {
594  $obj = $this->db->fetch_object($resql);
595  if ($obj) {
596  $this->id = $obj->rowid;
597  $this->ref = $obj->ref;
598 
599  $this->entity = $obj->entity;
600 
601  $this->total_ht = $obj->total_ht;
602  $this->total_tva = $obj->total_tva;
603  $this->total_ttc = $obj->total_ttc;
604  $this->localtax1 = $obj->total_localtax1; // For backward compatibility
605  $this->localtax2 = $obj->total_localtax2; // For backward compatibility
606  $this->total_localtax1 = $obj->total_localtax1;
607  $this->total_localtax2 = $obj->total_localtax2;
608 
609  $this->note_public = $obj->note_public;
610  $this->note_private = $obj->note_private;
611  $this->detail_refuse = $obj->detail_refuse;
612  $this->detail_cancel = $obj->detail_cancel;
613 
614  $this->date_debut = $this->db->jdate($obj->date_debut);
615  $this->date_fin = $this->db->jdate($obj->date_fin);
616  $this->date_valid = $this->db->jdate($obj->date_valid);
617  $this->date_approve = $this->db->jdate($obj->date_approve);
618  $this->date_create = $this->db->jdate($obj->date_create);
619  $this->date_modif = $this->db->jdate($obj->date_modif);
620  $this->date_refuse = $this->db->jdate($obj->date_refuse);
621  $this->date_cancel = $this->db->jdate($obj->date_cancel);
622 
623  $this->fk_user_creat = $obj->fk_user_creat;
624  $this->fk_user_author = $obj->fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
625  $this->fk_user_modif = $obj->fk_user_modif;
626  $this->fk_user_validator = $obj->fk_user_validator;
627  $this->fk_user_valid = $obj->fk_user_valid;
628  $this->fk_user_refuse = $obj->fk_user_refuse;
629  $this->fk_user_cancel = $obj->fk_user_cancel;
630  $this->fk_user_approve = $obj->fk_user_approve;
631 
632  $user_author = new User($this->db);
633  if ($this->fk_user_author > 0) {
634  $user_author->fetch($this->fk_user_author);
635  }
636 
637  $this->user_author_infos = dolGetFirstLastname($user_author->firstname, $user_author->lastname);
638 
639  $user_approver = new User($this->db);
640  if ($this->fk_user_approve > 0) {
641  $user_approver->fetch($this->fk_user_approve);
642  } elseif ($this->fk_user_validator > 0) {
643  $user_approver->fetch($this->fk_user_validator); // For backward compatibility
644  }
645  $this->user_validator_infos = dolGetFirstLastname($user_approver->firstname, $user_approver->lastname);
646 
647  $this->fk_statut = $obj->status; // deprecated
648  $this->status = $obj->status;
649  $this->fk_c_paiement = $obj->fk_c_paiement;
650  $this->paid = $obj->paid;
651 
652  if ($this->status == self::STATUS_APPROVED || $this->status == self::STATUS_CLOSED) {
653  $user_valid = new User($this->db);
654  if ($this->fk_user_valid > 0) {
655  $user_valid->fetch($this->fk_user_valid);
656  }
657  $this->user_valid_infos = dolGetFirstLastname($user_valid->firstname, $user_valid->lastname);
658  }
659 
660  $this->fetch_optionals();
661 
662  $result = $this->fetch_lines();
663 
664  return $result;
665  } else {
666  return 0;
667  }
668  } else {
669  $this->error = $this->db->lasterror();
670  return -1;
671  }
672  }
673 
674  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
685  public function set_paid($id, $fuser, $notrigger = 0)
686  {
687  // phpcs:enable
688  dol_syslog(get_class($this)."::set_paid is deprecated, use setPaid instead", LOG_NOTICE);
689  return $this->setPaid($id, $fuser, $notrigger);
690  }
691 
700  public function setPaid($id, $fuser, $notrigger = 0)
701  {
702  $error = 0;
703  $this->db->begin();
704 
705  $sql = "UPDATE ".MAIN_DB_PREFIX."expensereport";
706  $sql .= " SET fk_statut = ".self::STATUS_CLOSED.", paid=1";
707  $sql .= " WHERE rowid = ".((int) $id)." AND fk_statut = ".self::STATUS_APPROVED;
708 
709  dol_syslog(get_class($this)."::set_paid", LOG_DEBUG);
710  $resql = $this->db->query($sql);
711  if ($resql) {
712  if ($this->db->affected_rows($resql)) {
713  if (!$notrigger) {
714  // Call trigger
715  $result = $this->call_trigger('EXPENSE_REPORT_PAID', $fuser);
716 
717  if ($result < 0) {
718  $error++;
719  }
720  // End call triggers
721  }
722 
723  if (empty($error)) {
724  $this->db->commit();
725  return 1;
726  } else {
727  $this->db->rollback();
728  $this->error = $this->db->error();
729  return -2;
730  }
731  } else {
732  $this->db->commit();
733  return 0;
734  }
735  } else {
736  $this->db->rollback();
737  dol_print_error($this->db);
738  return -1;
739  }
740  }
741 
748  public function getLibStatut($mode = 0)
749  {
750  return $this->LibStatut($this->status, $mode);
751  }
752 
753  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
761  public function LibStatut($status, $mode = 0)
762  {
763  // phpcs:enable
764  global $langs;
765 
766  $labelStatus = $langs->transnoentitiesnoconv($this->statuts[$status]);
767  $labelStatusShort = $langs->transnoentitiesnoconv($this->statuts_short[$status]);
768 
769  $statusType = $this->statuts_logo[$status];
770 
771  return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode);
772  }
773 
774 
781  public function info($id)
782  {
783  global $conf;
784 
785  $sql = "SELECT f.rowid,";
786  $sql .= " f.date_create as datec,";
787  $sql .= " f.tms as date_modification,";
788  $sql .= " f.date_valid as datev,";
789  $sql .= " f.date_approve as datea,";
790  $sql .= " f.fk_user_creat as fk_user_creation,";
791  $sql .= " f.fk_user_author as fk_user_author,";
792  $sql .= " f.fk_user_modif as fk_user_modification,";
793  $sql .= " f.fk_user_valid,";
794  $sql .= " f.fk_user_approve";
795  $sql .= " FROM ".MAIN_DB_PREFIX."expensereport as f";
796  $sql .= " WHERE f.rowid = ".((int) $id);
797  $sql .= " AND f.entity = ".$conf->entity;
798 
799  $resql = $this->db->query($sql);
800  if ($resql) {
801  if ($this->db->num_rows($resql)) {
802  $obj = $this->db->fetch_object($resql);
803 
804  $this->id = $obj->rowid;
805 
806  $this->date_creation = $this->db->jdate($obj->datec);
807  $this->date_modification = $this->db->jdate($obj->date_modification);
808  $this->date_validation = $this->db->jdate($obj->datev);
809  $this->date_approbation = $this->db->jdate($obj->datea);
810 
811  $cuser = new User($this->db);
812  $cuser->fetch($obj->fk_user_author);
813  $this->user_creation = $cuser;
814 
815  if ($obj->fk_user_creation) {
816  $cuser = new User($this->db);
817  $cuser->fetch($obj->fk_user_creation);
818  $this->user_creation = $cuser;
819  }
820  if ($obj->fk_user_valid) {
821  $vuser = new User($this->db);
822  $vuser->fetch($obj->fk_user_valid);
823  $this->user_validation = $vuser;
824  }
825  if ($obj->fk_user_modification) {
826  $muser = new User($this->db);
827  $muser->fetch($obj->fk_user_modification);
828  $this->user_modification = $muser;
829  }
830  if ($obj->fk_user_approve) {
831  $auser = new User($this->db);
832  $auser->fetch($obj->fk_user_approve);
833  $this->user_approve = $auser;
834  }
835  }
836  $this->db->free($resql);
837  } else {
838  dol_print_error($this->db);
839  }
840  }
841 
842 
843 
851  public function initAsSpecimen()
852  {
853  global $user, $langs, $conf;
854 
855  $now = dol_now();
856 
857  // Initialise parametres
858  $this->id = 0;
859  $this->ref = 'SPECIMEN';
860  $this->specimen = 1;
861  $this->entity = 1;
862  $this->date_create = $now;
863  $this->date_debut = $now;
864  $this->date_fin = $now;
865  $this->date_valid = $now;
866  $this->date_approve = $now;
867 
868  $type_fees_id = 2; // TF_TRIP
869 
870  $this->status = 5;
871  $this->fk_statut = 5;
872 
873  $this->fk_user_author = $user->id;
874  $this->fk_user_validator = $user->id;
875  $this->fk_user_valid = $user->id;
876  $this->fk_user_approve = $user->id;
877 
878  $this->note_private = 'Private note';
879  $this->note_public = 'SPECIMEN';
880  $nbp = 5;
881  $xnbp = 0;
882  while ($xnbp < $nbp) {
883  $line = new ExpenseReportLine($this->db);
884  $line->comments = $langs->trans("Comment")." ".$xnbp;
885  $line->date = ($now - 3600 * (1 + $xnbp));
886  $line->total_ht = 100;
887  $line->total_tva = 20;
888  $line->total_ttc = 120;
889  $line->qty = 1;
890  $line->vatrate = 20;
891  $line->value_unit = 120;
892  $line->fk_expensereport = 0;
893  $line->type_fees_code = 'TRA';
894  $line->fk_c_type_fees = $type_fees_id;
895 
896  $line->projet_ref = 'ABC';
897 
898  $this->lines[$xnbp] = $line;
899  $xnbp++;
900 
901  $this->total_ht += $line->total_ht;
902  $this->total_tva += $line->total_tva;
903  $this->total_ttc += $line->total_ttc;
904  }
905  }
906 
907  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
915  public function fetch_line_by_project($projectid, $user = '')
916  {
917  // phpcs:enable
918  global $conf, $db, $langs;
919 
920  $langs->load('trips');
921 
922  if ($user->rights->expensereport->lire) {
923  $sql = "SELECT de.fk_expensereport, de.date, de.comments, de.total_ht, de.total_ttc";
924  $sql .= " FROM ".MAIN_DB_PREFIX."expensereport_det as de";
925  $sql .= " WHERE de.fk_projet = ".((int) $projectid);
926 
927  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
928  $result = $this->db->query($sql);
929  if ($result) {
930  $num = $this->db->num_rows($result);
931  $i = 0;
932  $total_HT = 0;
933  $total_TTC = 0;
934 
935  while ($i < $num) {
936  $objp = $this->db->fetch_object($result);
937 
938  $sql2 = "SELECT d.rowid, d.fk_user_author, d.ref, d.fk_statut as status";
939  $sql2 .= " FROM ".MAIN_DB_PREFIX."expensereport as d";
940  $sql2 .= " WHERE d.rowid = ".((int) $objp->fk_expensereport);
941 
942  $result2 = $this->db->query($sql2);
943  $obj = $this->db->fetch_object($result2);
944 
945  $objp->fk_user_author = $obj->fk_user_author;
946  $objp->ref = $obj->ref;
947  $objp->fk_c_expensereport_status = $obj->status;
948  $objp->rowid = $obj->rowid;
949 
950  $total_HT = $total_HT + $objp->total_ht;
951  $total_TTC = $total_TTC + $objp->total_ttc;
952  $author = new User($this->db);
953  $author->fetch($objp->fk_user_author);
954 
955  print '<tr>';
956  print '<td><a href="'.DOL_URL_ROOT.'/expensereport/card.php?id='.$objp->rowid.'">'.$objp->ref_num.'</a></td>';
957  print '<td class="center">'.dol_print_date($objp->date, 'day').'</td>';
958  print '<td>'.$author->getNomUrl(1).'</td>';
959  print '<td>'.$objp->comments.'</td>';
960  print '<td class="right">'.price($objp->total_ht).'</td>';
961  print '<td class="right">'.price($objp->total_ttc).'</td>';
962  print '<td class="right">';
963 
964  switch ($objp->fk_c_expensereport_status) {
965  case 4:
966  print img_picto($langs->trans('StatusOrderCanceled'), 'statut5');
967  break;
968  case 1:
969  print $langs->trans('Draft').' '.img_picto($langs->trans('Draft'), 'statut0');
970  break;
971  case 2:
972  print $langs->trans('TripForValid').' '.img_picto($langs->trans('TripForValid'), 'statut3');
973  break;
974  case 5:
975  print $langs->trans('TripForPaid').' '.img_picto($langs->trans('TripForPaid'), 'statut3');
976  break;
977  case 6:
978  print $langs->trans('TripPaid').' '.img_picto($langs->trans('TripPaid'), 'statut4');
979  break;
980  }
981  /*
982  if ($status==4) return img_picto($langs->trans('StatusOrderCanceled'),'statut5');
983  if ($status==1) return img_picto($langs->trans('StatusOrderDraft'),'statut0');
984  if ($status==2) return img_picto($langs->trans('StatusOrderValidated'),'statut1');
985  if ($status==2) return img_picto($langs->trans('StatusOrderOnProcess'),'statut3');
986  if ($status==5) return img_picto($langs->trans('StatusOrderToBill'),'statut4');
987  if ($status==6) return img_picto($langs->trans('StatusOrderOnProcess'),'statut6');
988  */
989  print '</td>';
990  print '</tr>';
991 
992  $i++;
993  }
994 
995  print '<tr class="liste_total"><td colspan="4">'.$langs->trans("Number").': '.$i.'</td>';
996  print '<td class="right" width="100">'.$langs->trans("TotalHT").' : '.price($total_HT).'</td>';
997  print '<td class="right" width="100">'.$langs->trans("TotalTTC").' : '.price($total_TTC).'</td>';
998  print '<td>&nbsp;</td>';
999  print '</tr>';
1000  } else {
1001  $this->error = $this->db->lasterror();
1002  return -1;
1003  }
1004  }
1005  }
1006 
1007  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1013  public function fetch_lines()
1014  {
1015  // phpcs:enable
1016  global $conf;
1017 
1018  $this->lines = array();
1019 
1020  $sql = ' SELECT de.rowid, de.comments, de.qty, de.value_unit, de.date, de.rang,';
1021  $sql .= " de.".$this->fk_element.", de.fk_c_type_fees, de.fk_c_exp_tax_cat, de.fk_projet as fk_project,";
1022  $sql .= ' de.tva_tx, de.vat_src_code,';
1023  $sql .= ' de.localtax1_tx, de.localtax2_tx, de.localtax1_type, de.localtax2_type,';
1024  $sql .= ' de.fk_ecm_files,';
1025  $sql .= ' de.total_ht, de.total_tva, de.total_ttc,';
1026  $sql .= ' de.total_localtax1, de.total_localtax2, de.rule_warning_message,';
1027  $sql .= ' ctf.code as code_type_fees, ctf.label as libelle_type_fees, ctf.accountancy_code as accountancy_code_type_fees,';
1028  $sql .= ' p.ref as ref_projet, p.title as title_projet';
1029  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line.' as de';
1030  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_type_fees as ctf ON de.fk_c_type_fees = ctf.id';
1031  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'projet as p ON de.fk_projet = p.rowid';
1032  $sql .= " WHERE de.".$this->fk_element." = ".((int) $this->id);
1033  if (!empty($conf->global->EXPENSEREPORT_LINES_SORTED_BY_ROWID)) {
1034  $sql .= ' ORDER BY de.rang ASC, de.rowid ASC';
1035  } else {
1036  $sql .= ' ORDER BY de.rang ASC, de.date ASC';
1037  }
1038 
1039  $resql = $this->db->query($sql);
1040  if ($resql) {
1041  $num = $this->db->num_rows($resql);
1042  $i = 0;
1043  while ($i < $num) {
1044  $objp = $this->db->fetch_object($resql);
1045 
1046  $deplig = new ExpenseReportLine($this->db);
1047 
1048  $deplig->rowid = $objp->rowid;
1049  $deplig->id = $objp->rowid;
1050  $deplig->comments = $objp->comments;
1051  $deplig->qty = $objp->qty;
1052  $deplig->value_unit = $objp->value_unit;
1053  $deplig->date = $objp->date;
1054  $deplig->dates = $this->db->jdate($objp->date);
1055 
1056  $deplig->fk_expensereport = $objp->fk_expensereport;
1057  $deplig->fk_c_type_fees = $objp->fk_c_type_fees;
1058  $deplig->fk_c_exp_tax_cat = $objp->fk_c_exp_tax_cat;
1059  $deplig->fk_projet = $objp->fk_project; // deprecated
1060  $deplig->fk_project = $objp->fk_project;
1061  $deplig->fk_ecm_files = $objp->fk_ecm_files;
1062 
1063  $deplig->total_ht = $objp->total_ht;
1064  $deplig->total_tva = $objp->total_tva;
1065  $deplig->total_ttc = $objp->total_ttc;
1066  $deplig->total_localtax1 = $objp->total_localtax1;
1067  $deplig->total_localtax2 = $objp->total_localtax2;
1068 
1069  $deplig->type_fees_code = empty($objp->code_type_fees) ? 'TF_OTHER' : $objp->code_type_fees;
1070  $deplig->type_fees_libelle = $objp->libelle_type_fees;
1071  $deplig->type_fees_accountancy_code = $objp->accountancy_code_type_fees;
1072 
1073  $deplig->tva_tx = $objp->tva_tx;
1074  $deplig->vatrate = $objp->tva_tx;
1075  $deplig->vat_src_code = $objp->vat_src_code;
1076  $deplig->localtax1_tx = $objp->localtax1_tx;
1077  $deplig->localtax2_tx = $objp->localtax2_tx;
1078  $deplig->localtax1_type = $objp->localtax1_type;
1079  $deplig->localtax2_type = $objp->localtax2_type;
1080 
1081  $deplig->projet_ref = $objp->ref_projet;
1082  $deplig->projet_title = $objp->title_projet;
1083 
1084  $deplig->rule_warning_message = $objp->rule_warning_message;
1085 
1086  $deplig->rang = $objp->rang;
1087 
1088  $this->lines[$i] = $deplig;
1089 
1090  $i++;
1091  }
1092  $this->db->free($resql);
1093  return 1;
1094  } else {
1095  $this->error = $this->db->lasterror();
1096  dol_syslog(get_class($this)."::fetch_lines: Error ".$this->error, LOG_ERR);
1097  return -3;
1098  }
1099  }
1100 
1101 
1109  public function delete(User $user = null, $notrigger = false)
1110  {
1111  global $conf;
1112  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1113 
1114  $error = 0;
1115 
1116  $this->db->begin();
1117 
1118  if (!$notrigger) {
1119  // Call trigger
1120  $result = $this->call_trigger('EXPENSE_REPORT_DELETE', $user);
1121  if ($result < 0) {
1122  $error++;
1123  }
1124  // End call triggers
1125  }
1126 
1127  // Delete extrafields of lines and lines
1128  if (!$error && !empty($this->table_element_line)) {
1129  $tabletodelete = $this->table_element_line;
1130  //$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).")";
1131  $sql = "DELETE FROM ".MAIN_DB_PREFIX.$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id);
1132  if (!$this->db->query($sql)) {
1133  $error++;
1134  $this->error = $this->db->lasterror();
1135  $this->errors[] = $this->error;
1136  dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
1137  }
1138  }
1139 
1140  if (!$error) {
1141  // Delete linked object
1142  $res = $this->deleteObjectLinked();
1143  if ($res < 0) {
1144  $error++;
1145  }
1146  }
1147 
1148  if (!$error) {
1149  // Delete linked contacts
1150  $res = $this->delete_linked_contact();
1151  if ($res < 0) {
1152  $error++;
1153  }
1154  }
1155 
1156  // Removed extrafields of object
1157  if (!$error) {
1158  $result = $this->deleteExtraFields();
1159  if ($result < 0) {
1160  $error++;
1161  dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
1162  }
1163  }
1164 
1165  // Delete main record
1166  if (!$error) {
1167  $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element." WHERE rowid = ".((int) $this->id);
1168  $res = $this->db->query($sql);
1169  if (!$res) {
1170  $error++;
1171  $this->error = $this->db->lasterror();
1172  $this->errors[] = $this->error;
1173  dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR);
1174  }
1175  }
1176 
1177  // Delete record into ECM index and physically
1178  if (!$error) {
1179  $res = $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
1180  if (!$res) {
1181  $error++;
1182  }
1183  }
1184 
1185  if (!$error) {
1186  // We remove directory
1187  $ref = dol_sanitizeFileName($this->ref);
1188  if ($conf->expensereport->multidir_output[$this->entity] && !empty($this->ref)) {
1189  $dir = $conf->expensereport->multidir_output[$this->entity]."/".$ref;
1190  $file = $dir."/".$ref.".pdf";
1191  if (file_exists($file)) {
1192  dol_delete_preview($this);
1193 
1194  if (!dol_delete_file($file, 0, 0, 0, $this)) {
1195  $this->error = 'ErrorFailToDeleteFile';
1196  $this->errors[] = $this->error;
1197  $this->db->rollback();
1198  return 0;
1199  }
1200  }
1201  if (file_exists($dir)) {
1202  $res = @dol_delete_dir_recursive($dir);
1203  if (!$res) {
1204  $this->error = 'ErrorFailToDeleteDir';
1205  $this->errors[] = $this->error;
1206  $this->db->rollback();
1207  return 0;
1208  }
1209  }
1210  }
1211  }
1212 
1213  if (!$error) {
1214  dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG);
1215  $this->db->commit();
1216  return 1;
1217  } else {
1218  $this->db->rollback();
1219  return -1;
1220  }
1221  }
1222 
1230  public function setValidate($fuser, $notrigger = 0)
1231  {
1232  global $conf, $langs, $user;
1233 
1234  $error = 0;
1235  $now = dol_now();
1236 
1237  // Protection
1238  if ($this->status == self::STATUS_VALIDATED) {
1239  dol_syslog(get_class($this)."::valid action abandonned: already validated", LOG_WARNING);
1240  return 0;
1241  }
1242 
1243  $this->date_valid = $now; // Required for the getNextNum later.
1244 
1245  // Define new ref
1246  if (!$error && (preg_match('/^[\‍(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
1247  $num = $this->getNextNumRef();
1248  } else {
1249  $num = $this->ref;
1250  }
1251  if (empty($num) || $num < 0) {
1252  return -1;
1253  }
1254 
1255  $this->newref = dol_sanitizeFileName($num);
1256 
1257  $this->db->begin();
1258 
1259  // Validate
1260  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
1261  $sql .= " SET ref = '".$this->db->escape($num)."',";
1262  $sql .= " fk_statut = ".self::STATUS_VALIDATED.",";
1263  $sql .= " date_valid = '".$this->db->idate($this->date_valid)."',";
1264  $sql .= " fk_user_valid = ".((int) $user->id);
1265  $sql .= " WHERE rowid = ".((int) $this->id);
1266 
1267  $resql = $this->db->query($sql);
1268  if ($resql) {
1269  if (!$error && !$notrigger) {
1270  // Call trigger
1271  $result = $this->call_trigger('EXPENSE_REPORT_VALIDATE', $fuser);
1272  if ($result < 0) {
1273  $error++;
1274  }
1275  // End call triggers
1276  }
1277 
1278  if (!$error) {
1279  $this->oldref = $this->ref;
1280 
1281  // Rename directory if dir was a temporary ref
1282  if (preg_match('/^[\‍(]?PROV/i', $this->ref)) {
1283  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1284 
1285  // Now we rename also files into index
1286  $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)."'";
1287  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'expensereport/".$this->db->escape($this->ref)."' AND entity = ".((int) $this->entity);
1288  $resql = $this->db->query($sql);
1289  if (!$resql) {
1290  $error++; $this->error = $this->db->lasterror();
1291  }
1292 
1293  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1294  $oldref = dol_sanitizeFileName($this->ref);
1295  $newref = dol_sanitizeFileName($num);
1296  $dirsource = $conf->expensereport->multidir_output[$this->entity].'/'.$oldref;
1297  $dirdest = $conf->expensereport->multidir_output[$this->entity].'/'.$newref;
1298  if (!$error && file_exists($dirsource)) {
1299  dol_syslog(get_class($this)."::setValidate() rename dir ".$dirsource." into ".$dirdest);
1300 
1301  if (@rename($dirsource, $dirdest)) {
1302  dol_syslog("Rename ok");
1303  // Rename docs starting with $oldref with $newref
1304  $listoffiles = dol_dir_list($dirdest, 'files', 1, '^'.preg_quote($oldref, '/'));
1305  foreach ($listoffiles as $fileentry) {
1306  $dirsource = $fileentry['name'];
1307  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1308  $dirsource = $fileentry['path'].'/'.$dirsource;
1309  $dirdest = $fileentry['path'].'/'.$dirdest;
1310  @rename($dirsource, $dirdest);
1311  }
1312  }
1313  }
1314  }
1315  }
1316 
1317  // Set new ref and current status
1318  if (!$error) {
1319  $this->ref = $num;
1320  $this->status = self::STATUS_VALIDATED;
1321  }
1322 
1323  if (empty($error)) {
1324  $this->db->commit();
1325  return 1;
1326  } else {
1327  $this->db->rollback();
1328  $this->error = $this->db->error();
1329  return -2;
1330  }
1331  } else {
1332  $this->db->rollback();
1333  $this->error = $this->db->lasterror();
1334  return -1;
1335  }
1336  }
1337 
1338  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1345  public function set_save_from_refuse($fuser)
1346  {
1347  // phpcs:enable
1348  global $conf, $langs;
1349 
1350  // Sélection de la date de début de la NDF
1351  $sql = 'SELECT date_debut';
1352  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element;
1353  $sql .= " WHERE rowid = ".((int) $this->id);
1354 
1355  $result = $this->db->query($sql);
1356 
1357  $objp = $this->db->fetch_object($result);
1358 
1359  $this->date_debut = $this->db->jdate($objp->date_debut);
1360 
1361  if ($this->status != self::STATUS_VALIDATED) {
1362  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1363  $sql .= " SET fk_statut = ".self::STATUS_VALIDATED;
1364  $sql .= " WHERE rowid = ".((int) $this->id);
1365 
1366  dol_syslog(get_class($this)."::set_save_from_refuse", LOG_DEBUG);
1367 
1368  if ($this->db->query($sql)) {
1369  return 1;
1370  } else {
1371  $this->error = $this->db->lasterror();
1372  return -1;
1373  }
1374  } else {
1375  dol_syslog(get_class($this)."::set_save_from_refuse expensereport already with save status", LOG_WARNING);
1376  }
1377  }
1378 
1386  public function setApproved($fuser, $notrigger = 0)
1387  {
1388  $now = dol_now();
1389  $error = 0;
1390 
1391  // date approval
1392  $this->date_approve = $now;
1393  if ($this->status != self::STATUS_APPROVED) {
1394  $this->db->begin();
1395 
1396  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1397  $sql .= " SET ref = '".$this->db->escape($this->ref)."', fk_statut = ".self::STATUS_APPROVED.", fk_user_approve = ".((int) $fuser->id).",";
1398  $sql .= " date_approve='".$this->db->idate($this->date_approve)."'";
1399  $sql .= " WHERE rowid = ".((int) $this->id);
1400  if ($this->db->query($sql)) {
1401  if (!$notrigger) {
1402  // Call trigger
1403  $result = $this->call_trigger('EXPENSE_REPORT_APPROVE', $fuser);
1404 
1405  if ($result < 0) {
1406  $error++;
1407  }
1408  // End call triggers
1409  }
1410 
1411  if (empty($error)) {
1412  $this->db->commit();
1413  return 1;
1414  } else {
1415  $this->db->rollback();
1416  $this->error = $this->db->error();
1417  return -2;
1418  }
1419  } else {
1420  $this->db->rollback();
1421  $this->error = $this->db->lasterror();
1422  return -1;
1423  }
1424  } else {
1425  dol_syslog(get_class($this)."::setApproved expensereport already with approve status", LOG_WARNING);
1426  }
1427 
1428  return 0;
1429  }
1430 
1439  public function setDeny($fuser, $details, $notrigger = 0)
1440  {
1441  $now = dol_now();
1442  $error = 0;
1443 
1444  // date de refus
1445  if ($this->status != self::STATUS_REFUSED) {
1446  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1447  $sql .= " SET ref = '".$this->db->escape($this->ref)."', fk_statut = ".self::STATUS_REFUSED.", fk_user_refuse = ".((int) $fuser->id).",";
1448  $sql .= " date_refuse='".$this->db->idate($now)."',";
1449  $sql .= " detail_refuse='".$this->db->escape($details)."',";
1450  $sql .= " fk_user_approve = NULL";
1451  $sql .= " WHERE rowid = ".((int) $this->id);
1452  if ($this->db->query($sql)) {
1453  $this->fk_statut = 99; // deprecated
1454  $this->status = 99;
1455  $this->fk_user_refuse = $fuser->id;
1456  $this->detail_refuse = $details;
1457  $this->date_refuse = $now;
1458 
1459  if (!$notrigger) {
1460  // Call trigger
1461  $result = $this->call_trigger('EXPENSE_REPORT_DENY', $fuser);
1462 
1463  if ($result < 0) {
1464  $error++;
1465  }
1466  // End call triggers
1467  }
1468 
1469  if (empty($error)) {
1470  $this->db->commit();
1471  return 1;
1472  } else {
1473  $this->db->rollback();
1474  $this->error = $this->db->error();
1475  return -2;
1476  }
1477  } else {
1478  $this->db->rollback();
1479  $this->error = $this->db->lasterror();
1480  return -1;
1481  }
1482  } else {
1483  dol_syslog(get_class($this)."::setDeny expensereport already with refuse status", LOG_WARNING);
1484  }
1485  }
1486 
1487  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1497  public function set_unpaid($fuser, $notrigger = 0)
1498  {
1499  // phpcs:enable
1500  dol_syslog(get_class($this)."::set_unpaid is deprecated, use setUnpaid instead", LOG_NOTICE);
1501  return $this->setUnpaid($fuser, $notrigger);
1502  }
1503 
1511  public function setUnpaid($fuser, $notrigger = 0)
1512  {
1513  $error = 0;
1514 
1515  if ($this->paid) {
1516  $this->db->begin();
1517 
1518  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1519  $sql .= " SET paid = 0, fk_statut = ".self::STATUS_APPROVED;
1520  $sql .= " WHERE rowid = ".((int) $this->id);
1521 
1522  dol_syslog(get_class($this)."::set_unpaid", LOG_DEBUG);
1523 
1524  if ($this->db->query($sql)) {
1525  if (!$notrigger) {
1526  // Call trigger
1527  $result = $this->call_trigger('EXPENSE_REPORT_UNPAID', $fuser);
1528 
1529  if ($result < 0) {
1530  $error++;
1531  }
1532  // End call triggers
1533  }
1534 
1535  if (empty($error)) {
1536  $this->db->commit();
1537  return 1;
1538  } else {
1539  $this->db->rollback();
1540  $this->error = $this->db->error();
1541  return -2;
1542  }
1543  } else {
1544  $this->db->rollback();
1545  $this->error = $this->db->error();
1546  return -1;
1547  }
1548  } else {
1549  dol_syslog(get_class($this)."::set_unpaid expensereport already with unpaid status", LOG_WARNING);
1550  }
1551  }
1552 
1553  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1562  public function set_cancel($fuser, $detail, $notrigger = 0)
1563  {
1564  // phpcs:enable
1565  $error = 0;
1566  $this->date_cancel = $this->db->idate(dol_now());
1567  if ($this->status != self::STATUS_CANCELED) {
1568  $this->db->begin();
1569 
1570  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1571  $sql .= " SET fk_statut = ".self::STATUS_CANCELED.", fk_user_cancel = ".((int) $fuser->id);
1572  $sql .= ", date_cancel='".$this->db->idate($this->date_cancel)."'";
1573  $sql .= " ,detail_cancel='".$this->db->escape($detail)."'";
1574  $sql .= " WHERE rowid = ".((int) $this->id);
1575 
1576  dol_syslog(get_class($this)."::set_cancel", LOG_DEBUG);
1577 
1578  if ($this->db->query($sql)) {
1579  if (!$notrigger) {
1580  // Call trigger
1581  $result = $this->call_trigger('EXPENSE_REPORT_CANCEL', $fuser);
1582 
1583  if ($result < 0) {
1584  $error++;
1585  }
1586  // End call triggers
1587  }
1588 
1589  if (empty($error)) {
1590  $this->db->commit();
1591  return 1;
1592  } else {
1593  $this->db->rollback();
1594  $this->error = $this->db->error();
1595  return -2;
1596  }
1597  } else {
1598  $this->db->rollback();
1599  $this->error = $this->db->error();
1600  return -1;
1601  }
1602  } else {
1603  dol_syslog(get_class($this)."::set_cancel expensereport already with cancel status", LOG_WARNING);
1604  }
1605  }
1606 
1612  public function getNextNumRef()
1613  {
1614  global $langs, $conf;
1615  $langs->load("trips");
1616 
1617  if (!empty($conf->global->EXPENSEREPORT_ADDON)) {
1618  $mybool = false;
1619 
1620  $file = $conf->global->EXPENSEREPORT_ADDON.".php";
1621  $classname = $conf->global->EXPENSEREPORT_ADDON;
1622 
1623  // Include file with class
1624  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
1625  foreach ($dirmodels as $reldir) {
1626  $dir = dol_buildpath($reldir."core/modules/expensereport/");
1627 
1628  // Load file with numbering class (if found)
1629  $mybool |= @include_once $dir.$file;
1630  }
1631 
1632  if ($mybool === false) {
1633  dol_print_error('', "Failed to include file ".$file);
1634  return '';
1635  }
1636 
1637  $obj = new $classname();
1638  $numref = $obj->getNextValue($this);
1639 
1640  if ($numref != "") {
1641  return $numref;
1642  } else {
1643  $this->error = $obj->error;
1644  $this->errors = $obj->errors;
1645  //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
1646  return -1;
1647  }
1648  } else {
1649  $this->error = "Error_EXPENSEREPORT_ADDON_NotDefined";
1650  return -2;
1651  }
1652  }
1653 
1666  public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $moretitle = '', $notooltip = 0, $save_lastsearch_value = -1)
1667  {
1668  global $langs, $conf, $hookmanager;
1669 
1670  $result = '';
1671 
1672  $url = DOL_URL_ROOT.'/expensereport/card.php?id='.$this->id;
1673 
1674  if ($short) {
1675  return $url;
1676  }
1677 
1678  $label = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("ExpenseReport").'</u>';
1679  if (isset($this->status)) {
1680  $label .= ' '.$this->getLibStatut(5);
1681  }
1682  if (!empty($this->ref)) {
1683  $label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1684  }
1685  if (!empty($this->total_ht)) {
1686  $label .= '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
1687  }
1688  if (!empty($this->total_tva)) {
1689  $label .= '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
1690  }
1691  if (!empty($this->total_ttc)) {
1692  $label .= '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
1693  }
1694  if ($moretitle) {
1695  $label .= ' - '.$moretitle;
1696  }
1697 
1698  if ($option != 'nolink') {
1699  // Add param to save lastsearch_values or not
1700  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1701  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1702  $add_save_lastsearch_values = 1;
1703  }
1704  if ($add_save_lastsearch_values) {
1705  $url .= '&save_lastsearch_values=1';
1706  }
1707  }
1708 
1709  $ref = $this->ref;
1710  if (empty($ref)) {
1711  $ref = $this->id;
1712  }
1713 
1714  $linkclose = '';
1715  if (empty($notooltip)) {
1716  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
1717  $label = $langs->trans("ShowExpenseReport");
1718  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1719  }
1720  $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
1721  $linkclose .= ' class="classfortooltip"';
1722  }
1723 
1724  $linkstart = '<a href="'.$url.'"';
1725  $linkstart .= $linkclose.'>';
1726  $linkend = '</a>';
1727 
1728  $result .= $linkstart;
1729  if ($withpicto) {
1730  $result .= img_object(($notooltip ? '' : $label), $this->picto, ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
1731  }
1732  if ($withpicto != 2) {
1733  $result .= ($max ? dol_trunc($ref, $max) : $ref);
1734  }
1735  $result .= $linkend;
1736 
1737  global $action;
1738  $hookmanager->initHooks(array($this->element . 'dao'));
1739  $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
1740  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1741  if ($reshook > 0) {
1742  $result = $hookmanager->resPrint;
1743  } else {
1744  $result .= $hookmanager->resPrint;
1745  }
1746  return $result;
1747  }
1748 
1749  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1757  public function update_totaux_add($ligne_total_ht, $ligne_total_tva)
1758  {
1759  // phpcs:enable
1760  $this->total_ht = $this->total_ht + $ligne_total_ht;
1761  $this->total_tva = $this->total_tva + $ligne_total_tva;
1762  $this->total_ttc = $this->total_ht + $this->total_tva;
1763 
1764  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
1765  $sql .= " total_ht = ".$this->total_ht;
1766  $sql .= " , total_ttc = ".$this->total_ttc;
1767  $sql .= " , total_tva = ".$this->total_tva;
1768  $sql .= " WHERE rowid = ".((int) $this->id);
1769 
1770  $result = $this->db->query($sql);
1771  if ($result) {
1772  return 1;
1773  } else {
1774  $this->error = $this->db->error();
1775  return -1;
1776  }
1777  }
1778 
1794  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)
1795  {
1796  global $conf, $langs, $mysoc;
1797 
1798  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);
1799 
1800  if ($this->status == self::STATUS_DRAFT) {
1801  if (empty($qty)) {
1802  $qty = 0;
1803  }
1804  if (empty($fk_c_type_fees) || $fk_c_type_fees < 0) {
1805  $fk_c_type_fees = 0;
1806  }
1807  if (empty($fk_c_exp_tax_cat) || $fk_c_exp_tax_cat < 0) {
1808  $fk_c_exp_tax_cat = 0;
1809  }
1810  if (empty($vatrate) || $vatrate < 0) {
1811  $vatrate = 0;
1812  }
1813  if (empty($date)) {
1814  $date = '';
1815  }
1816  if (empty($fk_project)) {
1817  $fk_project = 0;
1818  }
1819 
1820  $qty = price2num($qty);
1821  if (!preg_match('/\s*\‍((.*)\‍)/', $vatrate)) {
1822  $vatrate = price2num($vatrate); // $txtva can have format '5.0 (XXX)' or '5'
1823  }
1824  $up = price2num($up);
1825 
1826  $this->db->begin();
1827 
1828  $this->line = new ExpenseReportLine($this->db);
1829 
1830  // We don't know seller and buyer for expense reports
1831  $seller = $mysoc; // We use same than current company (expense report are often done in same country)
1832  $seller->tva_assuj = 1; // Most seller uses vat
1833  $buyer = new Societe($this->db);
1834 
1835  $localtaxes_type = getLocalTaxesFromRate($vatrate, 0, $buyer, $seller);
1836 
1837  $vat_src_code = '';
1838  $reg = array();
1839  if (preg_match('/\s*\‍((.*)\‍)/', $vatrate, $reg)) {
1840  $vat_src_code = $reg[1];
1841  $vatrate = preg_replace('/\s*\‍(.*\‍)/', '', $vatrate); // Remove code into vatrate.
1842  }
1843  $vatrate = preg_replace('/\*/', '', $vatrate);
1844 
1845  $tmp = calcul_price_total($qty, $up, 0, $vatrate, -1, -1, 0, 'TTC', 0, $type, $seller, $localtaxes_type);
1846 
1847  $this->line->value_unit = $up;
1848 
1849  $this->line->vat_src_code = $vat_src_code;
1850  $this->line->vatrate = price2num($vatrate);
1851  $this->line->localtax1_tx = $localtaxes_type[1];
1852  $this->line->localtax2_tx = $localtaxes_type[3];
1853  $this->line->localtax1_type = $localtaxes_type[0];
1854  $this->line->localtax2_type = $localtaxes_type[2];
1855 
1856  $this->line->total_ttc = $tmp[2];
1857  $this->line->total_ht = $tmp[0];
1858  $this->line->total_tva = $tmp[1];
1859  $this->line->total_localtax1 = $tmp[9];
1860  $this->line->total_localtax2 = $tmp[10];
1861 
1862  $this->line->fk_expensereport = $this->id;
1863  $this->line->qty = $qty;
1864  $this->line->date = $date;
1865  $this->line->fk_c_type_fees = $fk_c_type_fees;
1866  $this->line->fk_c_exp_tax_cat = $fk_c_exp_tax_cat;
1867  $this->line->comments = $comments;
1868  $this->line->fk_projet = $fk_project; // deprecated
1869  $this->line->fk_project = $fk_project;
1870 
1871  $this->line->fk_ecm_files = $fk_ecm_files;
1872 
1873  $this->applyOffset();
1874  $this->checkRules($type, $seller);
1875 
1876  $result = $this->line->insert(0, true);
1877  if ($result > 0) {
1878  $result = $this->update_price(1); // This method is designed to add line from user input so total calculation must be done using 'auto' mode.
1879  if ($result > 0) {
1880  $this->db->commit();
1881  return $this->line->id;
1882  } else {
1883  $this->db->rollback();
1884  return -1;
1885  }
1886  } else {
1887  $this->error = $this->line->error;
1888  dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
1889  $this->db->rollback();
1890  return -2;
1891  }
1892  } else {
1893  dol_syslog(get_class($this)."::addline status of expense report must be Draft to allow use of ->addline()", LOG_ERR);
1894  $this->error = 'ErrorExpenseNotDraft';
1895  return -3;
1896  }
1897  }
1898 
1906  public function checkRules($type = 0, $seller = '')
1907  {
1908  global $user, $conf, $db, $langs, $mysoc;
1909 
1910  $langs->load('trips');
1911 
1912  // We don't know seller and buyer for expense reports
1913  if (!is_object($seller)) {
1914  $seller = $mysoc; // We use same than current company (expense report are often done in same country)
1915  $seller->tva_assuj = 1; // Most seller uses vat
1916  }
1917 
1918  $expensereportrule = new ExpenseReportRule($db);
1919  $rulestocheck = $expensereportrule->getAllRule($this->line->fk_c_type_fees, $this->line->date, $this->fk_user_author);
1920 
1921  $violation = 0;
1922  $rule_warning_message_tab = array();
1923 
1924  $current_total_ttc = $this->line->total_ttc;
1925  $new_current_total_ttc = $this->line->total_ttc;
1926 
1927  // check if one is violated
1928  foreach ($rulestocheck as $rule) {
1929  if (in_array($rule->code_expense_rules_type, array('EX_DAY', 'EX_MON', 'EX_YEA'))) {
1930  $amount_to_test = $this->line->getExpAmount($rule, $this->fk_user_author, $rule->code_expense_rules_type);
1931  } else {
1932  $amount_to_test = $current_total_ttc; // EX_EXP
1933  }
1934 
1935  $amount_to_test = $amount_to_test - $current_total_ttc + $new_current_total_ttc; // if amount as been modified by a previous rule
1936 
1937  if ($amount_to_test > $rule->amount) {
1938  $violation++;
1939 
1940  if ($rule->restrictive) {
1941  $this->error = 'ExpenseReportConstraintViolationError';
1942  $this->errors[] = $this->error;
1943 
1944  $new_current_total_ttc -= $amount_to_test - $rule->amount; // ex, entered 16€, limit 12€, subtracts 4€;
1945  $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));
1946  } else {
1947  $this->error = 'ExpenseReportConstraintViolationWarning';
1948  $this->errors[] = $this->error;
1949 
1950  $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));
1951  }
1952 
1953  // No break, we sould test if another rule is violated
1954  }
1955  }
1956 
1957  $this->line->rule_warning_message = implode('\n', $rule_warning_message_tab);
1958 
1959  if ($violation > 0) {
1960  $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);
1961 
1962  $this->line->value_unit = $tmp[5];
1963  $this->line->total_ttc = $tmp[2];
1964  $this->line->total_ht = $tmp[0];
1965  $this->line->total_tva = $tmp[1];
1966  $this->line->total_localtax1 = $tmp[9];
1967  $this->line->total_localtax2 = $tmp[10];
1968 
1969  return false;
1970  } else {
1971  return true;
1972  }
1973  }
1974 
1982  public function applyOffset($type = 0, $seller = '')
1983  {
1984  global $conf, $mysoc;
1985 
1986  if (empty($conf->global->MAIN_USE_EXPENSE_IK)) {
1987  return false;
1988  }
1989 
1990  $userauthor = new User($this->db);
1991  if ($userauthor->fetch($this->fk_user_author) <= 0) {
1992  $this->error = 'ErrorCantFetchUser';
1993  $this->errors[] = 'ErrorCantFetchUser';
1994  return false;
1995  }
1996 
1997  // We don't know seller and buyer for expense reports
1998  if (!is_object($seller)) {
1999  $seller = $mysoc; // We use same than current company (expense report are often done in same country)
2000  $seller->tva_assuj = 1; // Most seller uses vat
2001  }
2002 
2003  $expenseik = new ExpenseReportIk($this->db);
2004  $range = $expenseik->getRangeByUser($userauthor, $this->line->fk_c_exp_tax_cat);
2005 
2006  if (empty($range)) {
2007  $this->error = 'ErrorNoRangeAvailable';
2008  $this->errors[] = 'ErrorNoRangeAvailable';
2009  return false;
2010  }
2011 
2012  if (!empty($conf->global->MAIN_EXPENSE_APPLY_ENTIRE_OFFSET)) {
2013  $ikoffset = $range->ikoffset;
2014  } else {
2015  $ikoffset = $range->ikoffset / 12; // The amount of offset is a global value for the year
2016  }
2017 
2018  // Test if ikoffset has been applied for the current month
2019  if (!$this->offsetAlreadyGiven()) {
2020  $new_up = $range->coef + ($ikoffset / $this->line->qty);
2021  $tmp = calcul_price_total($this->line->qty, $new_up, 0, $this->line->vatrate, 0, 0, 0, 'TTC', 0, $type, $seller);
2022 
2023  $this->line->value_unit = $tmp[5];
2024  $this->line->total_ttc = $tmp[2];
2025  $this->line->total_ht = $tmp[0];
2026  $this->line->total_tva = $tmp[1];
2027  $this->line->total_localtax1 = $tmp[9];
2028  $this->line->total_localtax2 = $tmp[10];
2029 
2030  return true;
2031  }
2032 
2033  return false;
2034  }
2035 
2041  public function offsetAlreadyGiven()
2042  {
2043  $sql = 'SELECT e.rowid FROM '.MAIN_DB_PREFIX.'expensereport e';
2044  $sql .= " INNER JOIN ".MAIN_DB_PREFIX."expensereport_det d ON (e.rowid = d.fk_expensereport)";
2045  $sql .= " INNER JOIN ".MAIN_DB_PREFIX."c_type_fees f ON (d.fk_c_type_fees = f.id AND f.code = 'EX_KME')";
2046  $sql .= " WHERE e.fk_user_author = ".(int) $this->fk_user_author;
2047  $sql .= " AND YEAR(d.date) = '".dol_print_date($this->line->date, '%Y')."' AND MONTH(d.date) = '".dol_print_date($this->line->date, '%m')."'";
2048  if (!empty($this->line->id)) {
2049  $sql .= ' AND d.rowid <> '.((int) $this->line->id);
2050  }
2051 
2052  dol_syslog(get_class($this)."::offsetAlreadyGiven");
2053  $resql = $this->db->query($sql);
2054  if ($resql) {
2055  $num = $this->db->num_rows($resql);
2056  if ($num > 0) {
2057  return true;
2058  }
2059  } else {
2060  dol_print_error($this->db);
2061  }
2062 
2063  return false;
2064  }
2065 
2083  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)
2084  {
2085  global $user, $mysoc;
2086 
2087  if ($this->status == self::STATUS_DRAFT || $this->status == self::STATUS_REFUSED) {
2088  $this->db->begin();
2089 
2090  $error = 0;
2091  $type = 0; // TODO What if type is service ?
2092 
2093  // We don't know seller and buyer for expense reports
2094  $seller = $mysoc; // We use same than current company (expense report are often done in same country)
2095  $seller->tva_assuj = 1; // Most seller uses vat
2096  $seller->localtax1_assuj = $mysoc->localtax1_assuj; // We don't know, we reuse the state of company
2097  $seller->localtax2_assuj = $mysoc->localtax1_assuj; // We don't know, we reuse the state of company
2098  $buyer = new Societe($this->db);
2099 
2100  $localtaxes_type = getLocalTaxesFromRate($vatrate, 0, $buyer, $seller);
2101 
2102  // Clean vat code
2103  $reg = array();
2104  $vat_src_code = '';
2105  if (preg_match('/\‍((.*)\‍)/', $vatrate, $reg)) {
2106  $vat_src_code = $reg[1];
2107  $vatrate = preg_replace('/\s*\‍(.*\‍)/', '', $vatrate); // Remove code into vatrate.
2108  }
2109  $vatrate = preg_replace('/\*/', '', $vatrate);
2110 
2111  $tmp = calcul_price_total($qty, $value_unit, 0, $vatrate, -1, -1, 0, 'TTC', 0, $type, $seller, $localtaxes_type);
2112  //var_dump($vatrate);var_dump($localtaxes_type);var_dump($tmp);exit;
2113  // calcul total of line
2114  //$total_ttc = price2num($qty*$value_unit, 'MT');
2115 
2116  $tx_tva = $vatrate / 100;
2117  $tx_tva = $tx_tva + 1;
2118 
2119  $this->line = new ExpenseReportLine($this->db);
2120  $this->line->comments = $comments;
2121  $this->line->qty = $qty;
2122  $this->line->value_unit = $value_unit;
2123  $this->line->date = $date;
2124 
2125  $this->line->fk_expensereport = $expensereport_id;
2126  $this->line->fk_c_type_fees = $type_fees_id;
2127  $this->line->fk_c_exp_tax_cat = $fk_c_exp_tax_cat;
2128  $this->line->fk_projet = $projet_id; // deprecated
2129  $this->line->fk_project = $projet_id;
2130 
2131  $this->line->vat_src_code = $vat_src_code;
2132  $this->line->vatrate = price2num($vatrate);
2133  $this->line->localtax1_tx = $localtaxes_type[1];
2134  $this->line->localtax2_tx = $localtaxes_type[3];
2135  $this->line->localtax1_type = $localtaxes_type[0];
2136  $this->line->localtax2_type = $localtaxes_type[2];
2137 
2138  $this->line->total_ttc = $tmp[2];
2139  $this->line->total_ht = $tmp[0];
2140  $this->line->total_tva = $tmp[1];
2141  $this->line->total_localtax1 = $tmp[9];
2142  $this->line->total_localtax2 = $tmp[10];
2143 
2144  $this->line->fk_ecm_files = $fk_ecm_files;
2145 
2146  $this->line->id = ((int) $rowid);
2147 
2148  // Select des infos sur le type fees
2149  $sql = "SELECT c.code as code_type_fees, c.label as libelle_type_fees";
2150  $sql .= " FROM ".MAIN_DB_PREFIX."c_type_fees as c";
2151  $sql .= " WHERE c.id = ".((int) $type_fees_id);
2152  $resql = $this->db->query($sql);
2153  if ($resql) {
2154  $objp_fees = $this->db->fetch_object($resql);
2155  $this->line->type_fees_code = $objp_fees->code_type_fees;
2156  $this->line->type_fees_libelle = $objp_fees->libelle_type_fees;
2157  $this->db->free($resql);
2158  }
2159 
2160  // Select des informations du projet
2161  $sql = "SELECT p.ref as ref_projet, p.title as title_projet";
2162  $sql .= " FROM ".MAIN_DB_PREFIX."projet as p";
2163  $sql .= " WHERE p.rowid = ".((int) $projet_id);
2164  $resql = $this->db->query($sql);
2165  if ($resql) {
2166  $objp_projet = $this->db->fetch_object($resql);
2167  $this->line->projet_ref = $objp_projet->ref_projet;
2168  $this->line->projet_title = $objp_projet->title_projet;
2169  $this->db->free($resql);
2170  }
2171 
2172  $this->applyOffset();
2173  $this->checkRules();
2174 
2175  $result = $this->line->update($user);
2176  if ($result < 0) {
2177  $error++;
2178  }
2179 
2180  if (!$error && !$notrigger) {
2181  // Call triggers
2182  $result = $this->call_trigger('EXPENSE_REPORT_DET_MODIFY', $user);
2183  if ($result < 0) {
2184  $error++;
2185  }
2186  // End call triggers
2187  }
2188 
2189  if (!$error) {
2190  $this->db->commit();
2191  return 1;
2192  } else {
2193  $this->error = $this->line->error;
2194  $this->errors = $this->line->errors;
2195  $this->db->rollback();
2196  return -2;
2197  }
2198  }
2199  }
2200 
2209  public function deleteline($rowid, $fuser = '', $notrigger = 0)
2210  {
2211  $error=0;
2212 
2213  $this->db->begin();
2214 
2215  if (!$notrigger) {
2216  // Call triggers
2217  $result = $this->call_trigger('EXPENSE_REPORT_DET_DELETE', $fuser);
2218  if ($result < 0) {
2219  $error++;
2220  }
2221  // End call triggers
2222  }
2223 
2224  $sql = ' DELETE FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2225  $sql .= ' WHERE rowid = '.((int) $rowid);
2226 
2227  dol_syslog(get_class($this)."::deleteline sql=".$sql);
2228  $result = $this->db->query($sql);
2229 
2230  if (!$result || $error > 0 ) {
2231  $this->error = $this->db->error();
2232  dol_syslog(get_class($this)."::deleteline Error ".$this->error, LOG_ERR);
2233  $this->db->rollback();
2234  return -1;
2235  }
2236 
2237  $this->update_price(1);
2238 
2239  $this->db->commit();
2240 
2241  return 1;
2242  }
2243 
2244  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2253  public function periode_existe($fuser, $date_debut, $date_fin)
2254  {
2255  // phpcs:enable
2256  $sql = "SELECT rowid, date_debut, date_fin";
2257  $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element;
2258  $sql .= " WHERE fk_user_author = '{$fuser->id}'";
2259 
2260  dol_syslog(get_class($this)."::periode_existe sql=".$sql);
2261  $result = $this->db->query($sql);
2262  if ($result) {
2263  $num_rows = $this->db->num_rows($result); $i = 0;
2264 
2265  if ($num_rows > 0) {
2266  $date_d_form = $date_debut;
2267  $date_f_form = $date_fin;
2268 
2269  while ($i < $num_rows) {
2270  $objp = $this->db->fetch_object($result);
2271 
2272  $date_d_req = $this->db->jdate($objp->date_debut); // 3
2273  $date_f_req = $this->db->jdate($objp->date_fin); // 4
2274 
2275  if (!($date_f_form < $date_d_req || $date_d_form > $date_f_req)) {
2276  return $objp->rowid;
2277  }
2278 
2279  $i++;
2280  }
2281 
2282  return 0;
2283  } else {
2284  return 0;
2285  }
2286  } else {
2287  $this->error = $this->db->lasterror();
2288  dol_syslog(get_class($this)."::periode_existe Error ".$this->error, LOG_ERR);
2289  return -1;
2290  }
2291  }
2292 
2293 
2294  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2302  {
2303  // phpcs:enable
2304  $users_validator = array();
2305 
2306  $sql = "SELECT DISTINCT ur.fk_user";
2307  $sql .= " FROM ".MAIN_DB_PREFIX."user_rights as ur, ".MAIN_DB_PREFIX."rights_def as rd";
2308  $sql .= " WHERE ur.fk_id = rd.id and rd.module = 'expensereport' AND rd.perms = 'approve'"; // Permission 'Approve';
2309  $sql .= " UNION";
2310  $sql .= " SELECT DISTINCT ugu.fk_user";
2311  $sql .= " FROM ".MAIN_DB_PREFIX."usergroup_user as ugu, ".MAIN_DB_PREFIX."usergroup_rights as ur, ".MAIN_DB_PREFIX."rights_def as rd";
2312  $sql .= " WHERE ugu.fk_usergroup = ur.fk_usergroup AND ur.fk_id = rd.id and rd.module = 'expensereport' AND rd.perms = 'approve'"; // Permission 'Approve';
2313  //print $sql;
2314 
2315  dol_syslog(get_class($this)."::fetch_users_approver_expensereport sql=".$sql);
2316  $result = $this->db->query($sql);
2317  if ($result) {
2318  $num_rows = $this->db->num_rows($result); $i = 0;
2319  while ($i < $num_rows) {
2320  $objp = $this->db->fetch_object($result);
2321  array_push($users_validator, $objp->fk_user);
2322  $i++;
2323  }
2324  return $users_validator;
2325  } else {
2326  $this->error = $this->db->lasterror();
2327  dol_syslog(get_class($this)."::fetch_users_approver_expensereport Error ".$this->error, LOG_ERR);
2328  return -1;
2329  }
2330  }
2331 
2343  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2344  {
2345  global $conf;
2346 
2347  $outputlangs->load("trips");
2348 
2349  if (!dol_strlen($modele)) {
2350  if (!empty($this->model_pdf)) {
2351  $modele = $this->model_pdf;
2352  } elseif (!empty($this->modelpdf)) { // deprecated
2353  $modele = $this->modelpdf;
2354  } elseif (!empty($conf->global->EXPENSEREPORT_ADDON_PDF)) {
2355  $modele = $conf->global->EXPENSEREPORT_ADDON_PDF;
2356  }
2357  }
2358 
2359  if (!empty($modele)) {
2360  $modelpath = "core/modules/expensereport/doc/";
2361 
2362  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2363  } else {
2364  return 0;
2365  }
2366  }
2367 
2374  public function listOfTypes($active = 1)
2375  {
2376  global $langs;
2377  $ret = array();
2378  $sql = "SELECT id, code, label";
2379  $sql .= " FROM ".MAIN_DB_PREFIX."c_type_fees";
2380  $sql .= " WHERE active = ".((int) $active);
2381  dol_syslog(get_class($this)."::listOfTypes", LOG_DEBUG);
2382  $result = $this->db->query($sql);
2383  if ($result) {
2384  $num = $this->db->num_rows($result);
2385  $i = 0;
2386  while ($i < $num) {
2387  $obj = $this->db->fetch_object($result);
2388  $ret[$obj->code] = (($langs->transnoentitiesnoconv($obj->code) != $obj->code) ? $langs->transnoentitiesnoconv($obj->code) : $obj->label);
2389  $i++;
2390  }
2391  } else {
2392  dol_print_error($this->db);
2393  }
2394  return $ret;
2395  }
2396 
2397  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2403  public function load_state_board()
2404  {
2405  // phpcs:enable
2406  global $conf, $user;
2407 
2408  $this->nb = array();
2409 
2410  $sql = "SELECT count(ex.rowid) as nb";
2411  $sql .= " FROM ".MAIN_DB_PREFIX."expensereport as ex";
2412  $sql .= " WHERE ex.fk_statut > 0";
2413  $sql .= " AND ex.entity IN (".getEntity('expensereport').")";
2414  if (empty($user->rights->expensereport->readall)) {
2415  $userchildids = $user->getAllChildIds(1);
2416  $sql .= " AND (ex.fk_user_author IN (".$this->db->sanitize(join(',', $userchildids)).")";
2417  $sql .= " OR ex.fk_user_validator IN (".$this->db->sanitize(join(',', $userchildids))."))";
2418  }
2419 
2420  $resql = $this->db->query($sql);
2421  if ($resql) {
2422  while ($obj = $this->db->fetch_object($resql)) {
2423  $this->nb["expensereports"] = $obj->nb;
2424  }
2425  $this->db->free($resql);
2426  return 1;
2427  } else {
2428  dol_print_error($this->db);
2429  $this->error = $this->db->error();
2430  return -1;
2431  }
2432  }
2433 
2434  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2442  public function load_board($user, $option = 'topay')
2443  {
2444  // phpcs:enable
2445  global $conf, $langs;
2446 
2447  if ($user->socid) {
2448  return -1; // protection pour eviter appel par utilisateur externe
2449  }
2450 
2451  $now = dol_now();
2452 
2453  $sql = "SELECT ex.rowid, ex.date_valid";
2454  $sql .= " FROM ".MAIN_DB_PREFIX."expensereport as ex";
2455  if ($option == 'toapprove') {
2456  $sql .= " WHERE ex.fk_statut = ".self::STATUS_VALIDATED;
2457  } else {
2458  $sql .= " WHERE ex.fk_statut = ".self::STATUS_APPROVED;
2459  }
2460  $sql .= " AND ex.entity IN (".getEntity('expensereport').")";
2461  if (empty($user->rights->expensereport->readall)) {
2462  $userchildids = $user->getAllChildIds(1);
2463  $sql .= " AND (ex.fk_user_author IN (".$this->db->sanitize(join(',', $userchildids)).")";
2464  $sql .= " OR ex.fk_user_validator IN (".$this->db->sanitize(join(',', $userchildids))."))";
2465  }
2466 
2467  $resql = $this->db->query($sql);
2468  if ($resql) {
2469  $langs->load("trips");
2470 
2471  $response = new WorkboardResponse();
2472  if ($option == 'toapprove') {
2473  $response->warning_delay = $conf->expensereport->approve->warning_delay / 60 / 60 / 24;
2474  $response->label = $langs->trans("ExpenseReportsToApprove");
2475  $response->labelShort = $langs->trans("ToApprove");
2476  $response->url = DOL_URL_ROOT.'/expensereport/list.php?mainmenu=hrm&amp;statut='.self::STATUS_VALIDATED;
2477  } else {
2478  $response->warning_delay = $conf->expensereport->payment->warning_delay / 60 / 60 / 24;
2479  $response->label = $langs->trans("ExpenseReportsToPay");
2480  $response->labelShort = $langs->trans("StatusToPay");
2481  $response->url = DOL_URL_ROOT.'/expensereport/list.php?mainmenu=hrm&amp;statut='.self::STATUS_APPROVED;
2482  }
2483  $response->img = img_object('', "trip");
2484 
2485  while ($obj = $this->db->fetch_object($resql)) {
2486  $response->nbtodo++;
2487 
2488  if ($option == 'toapprove') {
2489  if ($this->db->jdate($obj->date_valid) < ($now - $conf->expensereport->approve->warning_delay)) {
2490  $response->nbtodolate++;
2491  }
2492  } else {
2493  if ($this->db->jdate($obj->date_valid) < ($now - $conf->expensereport->payment->warning_delay)) {
2494  $response->nbtodolate++;
2495  }
2496  }
2497  }
2498 
2499  return $response;
2500  } else {
2501  dol_print_error($this->db);
2502  $this->error = $this->db->error();
2503  return -1;
2504  }
2505  }
2506 
2513  public function hasDelay($option)
2514  {
2515  global $conf;
2516 
2517  // Only valid expenses reports
2518  if ($option == 'toapprove' && $this->status != 2) {
2519  return false;
2520  }
2521  if ($option == 'topay' && $this->status != 5) {
2522  return false;
2523  }
2524 
2525  $now = dol_now();
2526  if ($option == 'toapprove') {
2527  return (!empty($this->datevalid) ? $this->datevalid : $this->date_valid) < ($now - $conf->expensereport->approve->warning_delay);
2528  } else {
2529  return (!empty($this->datevalid) ? $this->datevalid : $this->date_valid) < ($now - $conf->expensereport->payment->warning_delay);
2530  }
2531  }
2532 
2538  public function getVentilExportCompta()
2539  {
2540  $alreadydispatched = 0;
2541 
2542  $type = 'expense_report';
2543 
2544  $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);
2545  $resql = $this->db->query($sql);
2546  if ($resql) {
2547  $obj = $this->db->fetch_object($resql);
2548  if ($obj) {
2549  $alreadydispatched = $obj->nb;
2550  }
2551  } else {
2552  $this->error = $this->db->lasterror();
2553  return -1;
2554  }
2555 
2556  if ($alreadydispatched) {
2557  return 1;
2558  }
2559  return 0;
2560  }
2561 
2567  public function getSumPayments()
2568  {
2569  $table = 'payment_expensereport';
2570  $field = 'fk_expensereport';
2571 
2572  $sql = 'SELECT sum(amount) as amount';
2573  $sql .= ' FROM '.MAIN_DB_PREFIX.$table;
2574  $sql .= " WHERE ".$field." = ".((int) $this->id);
2575 
2576  dol_syslog(get_class($this)."::getSumPayments", LOG_DEBUG);
2577  $resql = $this->db->query($sql);
2578  if ($resql) {
2579  $obj = $this->db->fetch_object($resql);
2580  $this->db->free($resql);
2581  return (empty($obj->amount) ? 0 : $obj->amount);
2582  } else {
2583  $this->error = $this->db->lasterror();
2584  return -1;
2585  }
2586  }
2587 
2596  public function computeTotalKm($fk_cat, $qty, $tva)
2597  {
2598  global $langs,$user,$db,$conf;
2599 
2600 
2601  $cumulYearQty = 0;
2602  $ranges = array();
2603  $coef = 0;
2604 
2605 
2606  if ($fk_cat < 0) {
2607  $this->error = $langs->trans('ErrorBadParameterCat');
2608  return -1;
2609  }
2610 
2611  if ($qty <= 0) {
2612  $this->error = $langs->trans('ErrorBadParameterQty');
2613  return -1;
2614  }
2615 
2616  $currentUser = new User($db);
2617  $currentUser->fetch($this->fk_user);
2618  $currentUser->getrights('expensereport');
2619  //Clean
2620  $qty = price2num($qty);
2621 
2622  $sql = " SELECT r.range_ik, t.ikoffset, t.coef";
2623  $sql .= " FROM ".MAIN_DB_PREFIX."expensereport_ik t";
2624  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_exp_tax_range r ON r.rowid = t.fk_range";
2625  $sql .= " WHERE t.fk_c_exp_tax_cat = ".(int) $fk_cat;
2626  $sql .= " ORDER BY r.range_ik ASC";
2627 
2628  dol_syslog("expenseReport::computeTotalkm sql=".$sql, LOG_DEBUG);
2629 
2630  $result = $this->db->query($sql);
2631 
2632  if ($result) {
2633  if ($conf->global->EXPENSEREPORT_CALCULATE_MILEAGE_EXPENSE_COEFFICIENT_ON_CURRENT_YEAR) {
2634  $arrayDate = dol_getdate(dol_now());
2635  $sql = " SELECT count(n.qty) as cumul FROM ".MAIN_DB_PREFIX."expensereport_det n";
2636  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."expensereport e ON e.rowid = n.fk_expensereport";
2637  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_type_fees tf ON tf.id = n.fk_c_type_fees";
2638  $sql.= " WHERE e.fk_user_author = ".(int) $this->fk_user_author;
2639  $sql.= " AND YEAR(n.date) = ".(int) $arrayDate['year'];
2640  $sql.= " AND tf.code = 'EX_KME' ";
2641  $sql.= " AND e.fk_statut = ".(int) ExpenseReport::STATUS_VALIDATED;
2642 
2643  $resql = $this->db->query($sql);
2644 
2645  if ($resql) {
2646  $obj = $this->db->fetch_object($resql);
2647  $cumulYearQty = $obj->cumul;
2648  }
2649 
2650  $qty = $cumulYearQty + $qty;
2651  }
2652 
2653  $num = $this->db->num_rows($result);
2654 
2655  if ($num) {
2656  for ($i = 0; $i < $num; $i++) {
2657  $obj = $this->db->fetch_object($result);
2658 
2659  $ranges[$i] = $obj;
2660  }
2661 
2662 
2663  for ($i = 0; $i < $num; $i++) {
2664  if ($i < ($num - 1)) {
2665  if ($qty > $ranges[$i]->range_ik && $qty < $ranges[$i+1]->range_ik) {
2666  $coef = $ranges[$i]->coef;
2667  $offset = $ranges[$i]->ikoffset;
2668  }
2669  } else {
2670  if ($qty > $ranges[$i]->range_ik) {
2671  $coef = $ranges[$i]->coef;
2672  $offset = $ranges[$i]->ikoffset;
2673  }
2674  }
2675  }
2676  $total_ht = $coef;
2677  return $total_ht;
2678  } else {
2679  $this->error = $langs->trans('TaxUndefinedForThisCategory');
2680  return 0;
2681  }
2682  } else {
2683  $this->error = $this->db->error()." sql=".$sql;
2684 
2685  return -1;
2686  }
2687  }
2688 }
2689 
2690 
2695 {
2699  public $db;
2700 
2704  public $table_element = 'expensereport_det';
2705 
2709  public $error = '';
2710 
2714  public $rowid;
2715 
2716  public $comments;
2717  public $qty;
2718  public $value_unit;
2719  public $date;
2720 
2724  public $fk_c_type_fees;
2725 
2729  public $fk_c_exp_tax_cat;
2730 
2734  public $fk_projet;
2735 
2739  public $fk_expensereport;
2740 
2741  public $type_fees_code;
2742  public $type_fees_libelle;
2743  public $type_fees_accountancy_code;
2744 
2745  public $projet_ref;
2746  public $projet_title;
2747  public $rang;
2748 
2749  public $vatrate;
2750  public $vat_src_code;
2751  public $tva_tx;
2752  public $localtax1_tx;
2753  public $localtax2_tx;
2754  public $localtax1_type;
2755  public $localtax2_type;
2756 
2757  public $total_ht;
2758  public $total_tva;
2759  public $total_ttc;
2760  public $total_localtax1;
2761  public $total_localtax2;
2762 
2763  // Multicurrency
2767  public $fk_multicurrency;
2768 
2772  public $multicurrency_code;
2773  public $multicurrency_tx;
2774  public $multicurrency_total_ht;
2775  public $multicurrency_total_tva;
2776  public $multicurrency_total_ttc;
2777 
2781  public $fk_ecm_files;
2782 
2783  public $rule_warning_message;
2784 
2785 
2791  public function __construct($db)
2792  {
2793  $this->db = $db;
2794  }
2795 
2802  public function fetch($rowid)
2803  {
2804  $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,';
2805  $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,';
2806  $sql .= ' fde.localtax1_tx, fde.localtax2_tx, fde.localtax1_type, fde.localtax2_type, fde.total_localtax1, fde.total_localtax2, fde.rule_warning_message,';
2807  $sql .= ' ctf.code as type_fees_code, ctf.label as type_fees_libelle,';
2808  $sql .= ' pjt.rowid as projet_id, pjt.title as projet_title, pjt.ref as projet_ref';
2809  $sql .= ' FROM '.MAIN_DB_PREFIX.'expensereport_det as fde';
2810  $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.
2811  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'projet as pjt ON fde.fk_projet=pjt.rowid';
2812  $sql .= ' WHERE fde.rowid = '.((int) $rowid);
2813 
2814  $result = $this->db->query($sql);
2815 
2816  if ($result) {
2817  $objp = $this->db->fetch_object($result);
2818 
2819  $this->rowid = $objp->rowid;
2820  $this->id = $objp->rowid;
2821  $this->ref = $objp->ref;
2822  $this->fk_expensereport = $objp->fk_expensereport;
2823  $this->comments = $objp->comments;
2824  $this->qty = $objp->qty;
2825  $this->date = $objp->date;
2826  $this->dates = $this->db->jdate($objp->date);
2827  $this->value_unit = $objp->value_unit;
2828  $this->fk_c_type_fees = $objp->fk_c_type_fees;
2829  $this->fk_c_exp_tax_cat = $objp->fk_c_exp_tax_cat;
2830  $this->fk_projet = $objp->fk_project; // deprecated
2831  $this->fk_project = $objp->fk_project;
2832  $this->type_fees_code = $objp->type_fees_code;
2833  $this->type_fees_libelle = $objp->type_fees_libelle;
2834  $this->projet_ref = $objp->projet_ref;
2835  $this->projet_title = $objp->projet_title;
2836 
2837  $this->vatrate = $objp->vatrate;
2838  $this->vat_src_code = $objp->vat_src_code;
2839  $this->localtax1_tx = $objp->localtax1_tx;
2840  $this->localtax2_tx = $objp->localtax2_tx;
2841  $this->localtax1_type = $objp->localtax1_type;
2842  $this->localtax2_type = $objp->localtax2_type;
2843 
2844  $this->total_ht = $objp->total_ht;
2845  $this->total_tva = $objp->total_tva;
2846  $this->total_ttc = $objp->total_ttc;
2847  $this->total_localtax1 = $objp->total_localtax1;
2848  $this->total_localtax2 = $objp->total_localtax2;
2849 
2850  $this->fk_ecm_files = $objp->fk_ecm_files;
2851 
2852  $this->rule_warning_message = $objp->rule_warning_message;
2853 
2854  $this->db->free($result);
2855  } else {
2856  dol_print_error($this->db);
2857  }
2858  }
2859 
2867  public function insert($notrigger = 0, $fromaddline = false)
2868  {
2869  global $langs, $user, $conf;
2870 
2871  $error = 0;
2872 
2873  dol_syslog("ExpenseReportLine::Insert", LOG_DEBUG);
2874 
2875  // Clean parameters
2876  $this->comments = trim($this->comments);
2877  if (empty($this->value_unit)) {
2878  $this->value_unit = 0;
2879  }
2880  $this->qty = price2num($this->qty);
2881  $this->vatrate = price2num($this->vatrate);
2882  if (empty($this->fk_c_exp_tax_cat)) {
2883  $this->fk_c_exp_tax_cat = 0;
2884  }
2885 
2886  $this->db->begin();
2887 
2888  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'expensereport_det';
2889  $sql .= ' (fk_expensereport, fk_c_type_fees, fk_projet,';
2890  $sql .= ' tva_tx, vat_src_code,';
2891  $sql .= ' localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
2892  $sql .= ' comments, qty, value_unit,';
2893  $sql .= ' total_ht, total_tva, total_ttc,';
2894  $sql .= ' total_localtax1, total_localtax2,';
2895  $sql .= ' date, rule_warning_message, fk_c_exp_tax_cat, fk_ecm_files)';
2896  $sql .= " VALUES (".$this->db->escape($this->fk_expensereport).",";
2897  $sql .= " ".((int) $this->fk_c_type_fees).",";
2898  $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')).",";
2899  $sql .= " ".((float) $this->vatrate).",";
2900  $sql .= " '".$this->db->escape(empty($this->vat_src_code) ? '' : $this->vat_src_code)."',";
2901  $sql .= " ".((float) price2num($this->localtax1_tx)).",";
2902  $sql .= " ".((float) price2num($this->localtax2_tx)).",";
2903  $sql .= " '".$this->db->escape($this->localtax1_type)."',";
2904  $sql .= " '".$this->db->escape($this->localtax2_type)."',";
2905  $sql .= " '".$this->db->escape($this->comments)."',";
2906  $sql .= " ".((float) $this->qty).",";
2907  $sql .= " ".((float) $this->value_unit).",";
2908  $sql .= " ".((float) price2num($this->total_ht)).",";
2909  $sql .= " ".((float) price2num($this->total_tva)).",";
2910  $sql .= " ".((float) price2num($this->total_ttc)).",";
2911  $sql .= " ".((float) price2num($this->total_localtax1)).",";
2912  $sql .= " ".((float) price2num($this->total_localtax2)).",";
2913  $sql .= " '".$this->db->idate($this->date)."',";
2914  $sql .= " ".(empty($this->rule_warning_message) ? 'null' : "'".$this->db->escape($this->rule_warning_message)."'").",";
2915  $sql .= " ".((int) $this->fk_c_exp_tax_cat).",";
2916  $sql .= " ".($this->fk_ecm_files > 0 ? ((int) $this->fk_ecm_files) : 'null');
2917  $sql .= ")";
2918 
2919  $resql = $this->db->query($sql);
2920  if ($resql) {
2921  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'expensereport_det');
2922 
2923 
2924  if (!$error && !$notrigger) {
2925  // Call triggers
2926  $result = $this->call_trigger('EXPENSE_REPORT_DET_CREATE', $user);
2927  if ($result < 0) {
2928  $error++;
2929  }
2930  // End call triggers
2931  }
2932 
2933 
2934  if (!$fromaddline) {
2935  $tmpparent = new ExpenseReport($this->db);
2936  $tmpparent->fetch($this->fk_expensereport);
2937  $result = $tmpparent->update_price(1);
2938  if ($result < 0) {
2939  $error++;
2940  $this->error = $tmpparent->error;
2941  $this->errors = $tmpparent->errors;
2942  }
2943  }
2944  } else {
2945  $error++;
2946  }
2947 
2948  if (!$error) {
2949  $this->db->commit();
2950  return $this->id;
2951  } else {
2952  $this->error = $this->db->lasterror();
2953  dol_syslog("ExpenseReportLine::insert Error ".$this->error, LOG_ERR);
2954  $this->db->rollback();
2955  return -2;
2956  }
2957  }
2958 
2967  public function getExpAmount(ExpenseReportRule $rule, $fk_user, $mode = 'day')
2968  {
2969  $amount = 0;
2970 
2971  $sql = 'SELECT SUM(d.total_ttc) as total_amount';
2972  $sql .= ' FROM '.MAIN_DB_PREFIX.'expensereport_det d';
2973  $sql .= ' INNER JOIN '.MAIN_DB_PREFIX.'expensereport e ON (d.fk_expensereport = e.rowid)';
2974  $sql .= ' WHERE e.fk_user_author = '.((int) $fk_user);
2975  if (!empty($this->id)) {
2976  $sql .= ' AND d.rowid <> '.((int) $this->id);
2977  }
2978  $sql .= ' AND d.fk_c_type_fees = '.((int) $rule->fk_c_type_fees);
2979  if ($mode == 'day' || $mode == 'EX_DAY') {
2980  $sql .= " AND d.date = '".dol_print_date($this->date, '%Y-%m-%d')."'";
2981  } elseif ($mode == 'mon' || $mode == 'EX_MON') {
2982  $sql .= " AND DATE_FORMAT(d.date, '%Y-%m') = '".dol_print_date($this->date, '%Y-%m')."'"; // @todo DATE_FORMAT is forbidden
2983  } elseif ($mode == 'year' || $mode == 'EX_YEA') {
2984  $sql .= " AND DATE_FORMAT(d.date, '%Y') = '".dol_print_date($this->date, '%Y')."'"; // @todo DATE_FORMAT is forbidden
2985  }
2986 
2987  dol_syslog('ExpenseReportLine::getExpAmount');
2988 
2989  $resql = $this->db->query($sql);
2990  if ($resql) {
2991  $num = $this->db->num_rows($resql);
2992  if ($num > 0) {
2993  $obj = $this->db->fetch_object($resql);
2994  $amount = (double) $obj->total_amount;
2995  }
2996  } else {
2997  dol_print_error($this->db);
2998  }
2999 
3000  return $amount + $this->total_ttc;
3001  }
3002 
3009  public function update(User $user)
3010  {
3011  global $langs, $conf;
3012 
3013  $error = 0;
3014 
3015  // Clean parameters
3016  $this->comments = trim($this->comments);
3017  $this->vatrate = price2num($this->vatrate);
3018  $this->value_unit = price2num($this->value_unit);
3019  if (empty($this->fk_c_exp_tax_cat)) {
3020  $this->fk_c_exp_tax_cat = 0;
3021  }
3022 
3023  $this->db->begin();
3024 
3025  // Update line in database
3026  $sql = "UPDATE ".MAIN_DB_PREFIX."expensereport_det SET";
3027  $sql .= " comments='".$this->db->escape($this->comments)."'";
3028  $sql .= ", value_unit = ".((float) $this->value_unit);
3029  $sql .= ", qty=".((float) $this->qty);
3030  $sql .= ", date='".$this->db->idate($this->date)."'";
3031  $sql .= ", total_ht=".((float) price2num($this->total_ht, 'MT'));
3032  $sql .= ", total_tva=".((float) price2num($this->total_tva, 'MT'));
3033  $sql .= ", total_ttc=".((float) price2num($this->total_ttc, 'MT'));
3034  $sql .= ", total_localtax1=".((float) price2num($this->total_localtax1, 'MT'));
3035  $sql .= ", total_localtax2=".((float) price2num($this->total_localtax2, 'MT'));
3036  $sql .= ", tva_tx=".((float) $this->vatrate);
3037  $sql .= ", vat_src_code='".$this->db->escape($this->vat_src_code)."'";
3038  $sql .= ", localtax1_tx=".((float) $this->localtax1_tx);
3039  $sql .= ", localtax2_tx=".((float) $this->localtax2_tx);
3040  $sql .= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'";
3041  $sql .= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'";
3042  $sql .= ", rule_warning_message='".$this->db->escape($this->rule_warning_message)."'";
3043  $sql .= ", fk_c_exp_tax_cat=".$this->db->escape($this->fk_c_exp_tax_cat);
3044  $sql .= ", fk_ecm_files=".($this->fk_ecm_files > 0 ? ((int) $this->fk_ecm_files) : 'null');
3045  if ($this->fk_c_type_fees) {
3046  $sql .= ", fk_c_type_fees = ".((int) $this->fk_c_type_fees);
3047  } else {
3048  $sql .= ", fk_c_type_fees=null";
3049  }
3050  if ($this->fk_project > 0) {
3051  $sql .= ", fk_projet=".((int) $this->fk_project);
3052  } else {
3053  $sql .= ", fk_projet=null";
3054  }
3055  $sql .= " WHERE rowid = ".((int) ($this->rowid ? $this->rowid : $this->id));
3056 
3057  dol_syslog("ExpenseReportLine::update");
3058 
3059  $resql = $this->db->query($sql);
3060  if ($resql) {
3061  $tmpparent = new ExpenseReport($this->db);
3062  $result = $tmpparent->fetch($this->fk_expensereport);
3063  if ($result > 0) {
3064  $result = $tmpparent->update_price(1);
3065  if ($result < 0) {
3066  $error++;
3067  $this->error = $tmpparent->error;
3068  $this->errors = $tmpparent->errors;
3069  }
3070  } else {
3071  $error++;
3072  $this->error = $tmpparent->error;
3073  $this->errors = $tmpparent->errors;
3074  }
3075  } else {
3076  $error++;
3077  dol_print_error($this->db);
3078  }
3079 
3080  if (!$error) {
3081  $this->db->commit();
3082  return 1;
3083  } else {
3084  $this->error = $this->db->lasterror();
3085  dol_syslog("ExpenseReportLine::update Error ".$this->error, LOG_ERR);
3086  $this->db->rollback();
3087  return -2;
3088  }
3089  }
3090 
3091  // ajouter ici comput_ ...
3092 }
$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.
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.
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.
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:47
if(isModEnabled('facture') &&!empty($user->rights->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') &&!empty($user->rights->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)) $resql
Social contributions to pay.
Definition: index.php:745
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:1402
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:1251
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:61
dol_delete_preview($object)
Delete all preview files linked to object instance.
Definition: files.lib.php:1454
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.
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
$conf db
API class for accounts.
Definition: inc.php:41