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