dolibarr 23.0.4
api_expensereports.class.php
1<?php
2/* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
3 * Copyright (C) 2016 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2020-2025 Frédéric France <frederic.france@free.fr>
5 * Copyright (C) 2025 MDW <mdeweerd@users.noreply.github.com>
6 * Copyright (C) 2025 William Mead <william@m34d.com>
7 * Copyright (C) 2025 Kowal Jessica <jessicakowal69@gmail.com>
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
23use Luracast\Restler\RestException;
24
25require_once DOL_DOCUMENT_ROOT.'/expensereport/class/expensereport.class.php';
26require_once DOL_DOCUMENT_ROOT.'/expensereport/class/paymentexpensereport.class.php';
27require_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
28
38{
42 public static $FIELDS = array(
43 'fk_user_author',
44 'date_debut',
45 'date_fin',
46 );
47
51 public static $FIELDSLINE = array(
52 'date',
53 'fk_c_type_fees',
54 'qty',
55 'value_unit',
56 'vatrate'
57 );
58
62 public static $FIELDSPAYMENT = array(
63 "fk_typepayment",
64 'datep',
65 'amounts',
66 );
67
71 public $expensereport;
72
76 public static $FIELDSNOTUPDATABLE = array(
77 'fk_statut', 'status', 'statut', 'paid',
78 'fk_user_valid', 'fk_user_approve', 'fk_user_refuse', 'fk_user_cancel', 'fk_user_creat', 'fk_user_modif',
79 'date_valid', 'date_approve', 'date_refuse', 'date_cancel', 'detail_refuse', 'detail_cancel',
80 'total_ht', 'total_tva', 'total_ttc', 'total_localtax1', 'total_localtax2',
81 'ref', 'entity',
82 );
83
84
88 public function __construct()
89 {
90 global $db;
91
92 $this->db = $db;
93 $this->expensereport = new ExpenseReport($this->db);
94 }
95
108 public function get($id)
109 {
110 if (!DolibarrApiAccess::$user->hasRight('expensereport', 'lire')) {
111 throw new RestException(403);
112 }
113
114 $result = $this->expensereport->fetch($id);
115 if (!$result) {
116 throw new RestException(404, 'Expense report not found');
117 }
118
119 if (!DolibarrApi::_checkAccessToResource('expensereport', $this->expensereport)) {
120 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
121 }
122
123 $this->expensereport->fetchObjectLinked();
124 return $this->_cleanObjectDatas($this->expensereport);
125 }
126
148 public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $user_ids = '', $sqlfilters = '', $properties = '', $pagination_data = false)
149 {
150 if (!DolibarrApiAccess::$user->hasRight('expensereport', 'lire')) {
151 throw new RestException(403);
152 }
153
154 $obj_ret = array();
155
156 // case of external user, $societe param is ignored and replaced by user's socid
157 //$socid = DolibarrApiAccess::$user->socid ?: $societe;
158
159 $sql = "SELECT t.rowid";
160 $sql .= " FROM ".MAIN_DB_PREFIX."expensereport AS t LEFT JOIN ".MAIN_DB_PREFIX."expensereport_extrafields AS ef ON (ef.fk_object = t.rowid)"; // Modification VMR Global Solutions to include extrafields as search parameters in the API GET call, so we will be able to filter on extrafields
161 $sql .= ' WHERE t.entity IN ('.getEntity('expensereport').')';
162 if ($user_ids) {
163 $sql .= " AND t.fk_user_author IN (".$this->db->sanitize($user_ids).")";
164 }
165 // $user_ids is provided by the caller, so it can not be the only owner filter. Narrow the result
166 // set on the hierarchy of the caller, with the same condition as expensereport/list.php.
167 if (!DolibarrApiAccess::$user->hasRight('expensereport', 'readall')
168 && (!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') || !DolibarrApiAccess::$user->hasRight('expensereport', 'writeall_advance'))) {
169 $childids = DolibarrApiAccess::$user->getAllChildIds(1);
170 $sql .= " AND t.fk_user_author IN (".$this->db->sanitize(implode(',', $childids)).")";
171 }
172
173 // Add sql filters
174 if ($sqlfilters) {
175 $errormessage = '';
176 $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
177 if ($errormessage) {
178 throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage);
179 }
180 }
181
182 //this query will return total orders with the filters given
183 $sqlTotals = str_replace('SELECT t.rowid', 'SELECT count(t.rowid) as total', $sql);
184
185 $sql .= $this->db->order($sortfield, $sortorder);
186 if ($limit) {
187 if ($page < 0) {
188 $page = 0;
189 }
190 $offset = $limit * $page;
191
192 $sql .= $this->db->plimit($limit + 1, $offset);
193 }
194
195 $result = $this->db->query($sql);
196
197 if ($result) {
198 $num = $this->db->num_rows($result);
199 $min = min($num, ($limit <= 0 ? $num : $limit));
200 $i = 0;
201 while ($i < $min) {
202 $obj = $this->db->fetch_object($result);
203 $expensereport_static = new ExpenseReport($this->db);
204 if ($expensereport_static->fetch($obj->rowid)) {
205 $obj_ret[] = $this->_filterObjectProperties($this->_cleanObjectDatas($expensereport_static), $properties);
206 }
207 $i++;
208 }
209 } else {
210 throw new RestException(503, 'Error when retrieve Expense Report list : '.$this->db->lasterror());
211 }
212
213 //if $pagination_data is true the response will contain element data with all values and element pagination with pagination data(total,page,limit)
214 if ($pagination_data) {
215 $totalsResult = $this->db->query($sqlTotals);
216 $total = $this->db->fetch_object($totalsResult)->total;
217
218 $tmp = $obj_ret;
219 $obj_ret = [];
220
221 $obj_ret['data'] = $tmp;
222 $obj_ret['pagination'] = [
223 'total' => (int) $total,
224 'page' => $page, //count starts from 0
225 'page_count' => ceil((int) $total / $limit),
226 'limit' => $limit
227 ];
228 }
229
230 return $obj_ret;
231 }
232
245 public function post($request_data = null)
246 {
247 if (!DolibarrApiAccess::$user->hasRight('expensereport', 'creer')) {
248 throw new RestException(403, "Insufficiant rights");
249 }
250
251 // Check mandatory fields
252 $result = $this->_validate($request_data);
253
254 // Same rule as expensereport/card.php: a report can be created only for the user or a user of his hierarchy
255 $fk_user_author = (int) ($request_data['fk_user_author'] ?? 0);
256 if ($fk_user_author > 0 && $fk_user_author != DolibarrApiAccess::$user->id
257 && (!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') || !DolibarrApiAccess::$user->hasRight('expensereport', 'writeall_advance'))
258 && !in_array($fk_user_author, DolibarrApiAccess::$user->getAllChildIds(1))) {
259 throw new RestException(403, 'User '.$fk_user_author.' is not in the hierarchy of login '.DolibarrApiAccess::$user->login);
260 }
261 // Exclude properties that must be set by the workflow methods or computed from the lines
262 foreach (ExpenseReports::$FIELDSNOTUPDATABLE as $field) {
263 unset($request_data[$field]);
264 }
265
266 foreach ($request_data as $field => $value) {
267 if ($field === 'caller') {
268 // Add a mention of caller so on trigger called after action, we can filter to avoid a loop if we try to sync back again with the caller
269 $this->expensereport->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
270 continue;
271 }
272
273 $this->expensereport->$field = $this->_checkValForAPI($field, $value, $this->expensereport);
274 }
275 /*if (isset($request_data["lines"])) {
276 $lines = array();
277 foreach ($request_data["lines"] as $line) {
278 array_push($lines, (object) $line);
279 }
280 $this->expensereport->lines = $lines;
281 }*/
282 if ($this->expensereport->create(DolibarrApiAccess::$user) < 0) {
283 throw new RestException(500, "Error creating expensereport", array_merge(array($this->expensereport->error), $this->expensereport->errors));
284 }
285
286 return $this->expensereport->id;
287 }
288
305 public function getLines($id)
306 {
307 if (!DolibarrApiAccess::$user->hasRight('expensereport', 'lire')) {
308 throw new RestException(403);
309 }
310
311 $result = $this->expensereport->fetch($id);
312 if (!$result) {
313 throw new RestException(404, 'Expense report not found');
314 }
315
316 if (!DolibarrApi::_checkAccessToResource('expensereport', $this->expensereport)) {
317 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
318 }
319 $this->expensereport->fetch_lines();
320 $result = array();
321 foreach ($this->expensereport->lines as $line) {
322 $result[] = $this->_cleanObjectDatas($line);
323 }
324 return $result;
325 }
326
343 public function postLine($id, $request_data = null)
344 {
345 if (!DolibarrApiAccess::$user->hasRight('expensereport', 'creer')) {
346 throw new RestException(403);
347 }
348
349 $result = $this->_validateLine($request_data);
350
351 $result = $this->expensereport->fetch($id);
352 if (!$result) {
353 throw new RestException(404, 'Expense report not found');
354 }
355
356 if (!DolibarrApi::_checkAccessToResource('expensereport', $this->expensereport)) {
357 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
358 }
359
360 if ($this->expensereport->status != ExpenseReport::STATUS_DRAFT) {
361 throw new RestException(403, 'Expense report must be in draft status to add lines');
362 }
363
364 $request_data = (object) $request_data;
365
366 $request_data->comments = sanitizeVal($request_data->comments, 'restricthtml');
367
368 $result = $this->expensereport->addline(
369 $request_data->qty,
370 $request_data->value_unit,
371 (int) $request_data->fk_c_type_fees,
372 $request_data->vatrate,
373 $request_data->date,
374 $request_data->comments,
375 $request_data->fk_project,
376 $request_data->fk_c_exp_tax_cat,
377 $request_data->type,
378 $request_data->fk_ecm_files
379 );
380
381 if ($result > 0) {
382 return $result;
383 } else {
384 throw new RestException(500, 'Error adding line to expense report: '.$this->expensereport->error);
385 }
386 }
387
407 public function putLine($id, $lineid, $request_data = null)
408 {
409 if (!DolibarrApiAccess::$user->hasRight('expensereport', 'creer')) {
410 throw new RestException(403);
411 }
412
413 $result = $this->expensereport->fetch($id);
414 if (!$result) {
415 throw new RestException(404, 'Expense report not found');
416 }
417
418 if (!DolibarrApi::_checkAccessToResource('expensereport', $this->expensereport)) {
419 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
420 }
421
422 if ($this->expensereport->status != ExpenseReport::STATUS_DRAFT) {
423 throw new RestException(403, 'Expense report must be in draft status to update lines');
424 }
425
426 $line = new ExpenseReportLine($this->db);
427 $result = $line->fetch($lineid);
428 if ($result <= 0) {
429 throw new RestException(404, 'Expense report line not found');
430 }
431
432 $request_data = (object) $request_data;
433
434 $request_data->comments = sanitizeVal($request_data->comments, 'restricthtml');
435
436 $updateRes = $this->expensereport->updateline(
437 $lineid,
438 (int) $request_data->fk_c_type_fees,
439 $request_data->fk_project,
440 $request_data->vatrate,
441 $request_data->comments,
442 $request_data->qty,
443 $request_data->value_unit,
444 $request_data->date,
445 $id,
446 $request_data->fk_c_exp_tax_cat,
447 $request_data->fk_ecm_files
448 );
449
450 if ($updateRes > 0) {
451 $result = $this->get($id);
452 unset($result->line);
453 return $this->_cleanObjectDatas($result);
454 } else {
455 throw new RestException(500, 'Error updating line: '.$this->expensereport->error);
456 }
457 }
458
475 public function deleteLine($id, $lineid)
476 {
477 if (!DolibarrApiAccess::$user->hasRight('expensereport', 'creer')) {
478 throw new RestException(403);
479 }
480
481 $result = $this->expensereport->fetch($id);
482 if (!$result) {
483 throw new RestException(404, 'Expense report not found');
484 }
485
486 if (!DolibarrApi::_checkAccessToResource('expensereport', $this->expensereport)) {
487 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
488 }
489
490 // Check if line exists
491 $lineExists = false;
492 $this->expensereport->fetch_lines();
493 foreach ($this->expensereport->lines as $line) {
494 if ($line->id == $lineid) {
495 $lineExists = true;
496 break;
497 }
498 }
499
500 if (!$lineExists) {
501 throw new RestException(404, 'Line not found');
502 }
503
504 if ($this->expensereport->status != ExpenseReport::STATUS_DRAFT) {
505 throw new RestException(403, 'Expense report must be in draft status to delete lines');
506 }
507
508 $result = $this->expensereport->deleteLine($lineid);
509 if ($result > 0) {
510 return $this->get($id);
511 } else {
512 throw new RestException(500, 'Error deleting line: '.$this->expensereport->error);
513 }
514 }
515
533 public function put($id, $request_data = null)
534 {
535 if (!DolibarrApiAccess::$user->hasRight('expensereport', 'creer')) {
536 throw new RestException(403);
537 }
538
539 $result = $this->expensereport->fetch($id);
540 if (!$result) {
541 throw new RestException(404, 'Expense report not found');
542 }
543
544 if (!DolibarrApi::_checkAccessToResource('expensereport', $this->expensereport)) {
545 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
546 }
547 // Same rule as expensereport/card.php: only a draft or a refused report can be modified
548 if (!in_array($this->expensereport->status, array(ExpenseReport::STATUS_DRAFT, ExpenseReport::STATUS_REFUSED))) {
549 throw new RestException(403, 'Only a draft or refused expense report can be modified');
550 }
551 // Exclude properties that must be set by the workflow methods or computed from the lines
552 foreach (ExpenseReports::$FIELDSNOTUPDATABLE as $field) {
553 unset($request_data[$field]);
554 }
555 foreach ($request_data as $field => $value) {
556 if ($field == 'id') {
557 continue;
558 }
559 if ($field === 'caller') {
560 // Add a mention of caller so on trigger called after action, we can filter to avoid a loop if we try to sync back again with the caller
561 $this->expensereport->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
562 continue;
563 }
564
565 if ($field == 'array_options' && is_array($value)) {
566 foreach ($value as $index => $val) {
567 $this->expensereport->array_options[$index] = $this->_checkValForAPI($field, $val, $this->expensereport);
568 }
569 continue;
570 }
571
572 if (!in_array($field, array('fk_statut', 'fk_user_approve'))) { // Exclude properties that must be set by other workflow methods
573 $this->expensereport->$field = $this->_checkValForAPI($field, $value, $this->expensereport);
574 }
575 }
576
577 if ($this->expensereport->update(DolibarrApiAccess::$user) > 0) {
578 return $this->get($id);
579 } else {
580 throw new RestException(500, $this->expensereport->error);
581 }
582 }
583
596 public function delete($id)
597 {
598 if (!DolibarrApiAccess::$user->hasRight('expensereport', 'supprimer')) {
599 throw new RestException(403);
600 }
601
602 $result = $this->expensereport->fetch($id);
603 if (!$result) {
604 throw new RestException(404, 'Expense report not found');
605 }
606
607 if (!DolibarrApi::_checkAccessToResource('expensereport', $this->expensereport)) {
608 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
609 }
610
611 if (!$this->expensereport->delete(DolibarrApiAccess::$user)) {
612 throw new RestException(500, 'Error when delete Expense Report : '.$this->expensereport->error);
613 }
614
615 return array(
616 'success' => array(
617 'code' => 200,
618 'message' => 'Expense Report deleted'
619 )
620 );
621 }
622
638 public function setToDraft($id)
639 {
640 if (!DolibarrApiAccess::$user->hasRight('expensereport', 'creer')) {
641 throw new RestException(403, "Insufficiant rights");
642 }
643 $result = $this->expensereport->fetch($id);
644 if (!$result) {
645 throw new RestException(404, 'Expense report not found');
646 }
647
648 if (!DolibarrApi::_checkAccessToResource('expensereport', $this->expensereport)) {
649 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
650 }
651
652 $result = $this->expensereport->setStatut(0);
653 if ($result == 0) {
654 throw new RestException(304, 'Error nothing done. May be object is already draft');
655 }
656 if ($result < 0) {
657 throw new RestException(500, 'Error when setting to draft expense report: '.$this->expensereport->error);
658 }
659
660 return $this->_cleanObjectDatas($this->expensereport);
661 }
662
682 public function validate($id, $notrigger = 0)
683 {
684 if (!DolibarrApiAccess::$user->hasRight('expensereport', 'creer')) {
685 throw new RestException(403, "Insufficiant rights");
686 }
687 $result = $this->expensereport->fetch($id);
688 if (!$result) {
689 throw new RestException(404, 'Expense report not found');
690 }
691
692 if (!DolibarrApi::_checkAccessToResource('expensereport', $this->expensereport)) {
693 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
694 }
695
696 $result = $this->expensereport->setValidate(DolibarrApiAccess::$user, $notrigger);
697 if ($result == 0) {
698 throw new RestException(304, 'Error nothing done. May be object is already validated');
699 }
700 if ($result < 0) {
701 throw new RestException(500, 'Error when validating expense report: '.$this->expensereport->error);
702 }
703
704 return $this->_cleanObjectDatas($this->expensereport);
705 }
706
707
727 public function approve($id, $notrigger = 0)
728 {
729 if (!DolibarrApiAccess::$user->hasRight('expensereport', 'approve')) {
730 throw new RestException(403, "Insufficiant rights");
731 }
732 $result = $this->expensereport->fetch($id);
733 if (!$result) {
734 throw new RestException(404, 'Expense report not found');
735 }
736
737 if (!DolibarrApi::_checkAccessToResource('expensereport', $this->expensereport)) {
738 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
739 }
740
741 $result = $this->expensereport->setApproved(DolibarrApiAccess::$user, $notrigger);
742 if ($result == 0) {
743 throw new RestException(304, 'Error nothing done. May be object is already approved');
744 }
745 if ($result < 0) {
746 throw new RestException(500, 'Error when approving expense report: '.$this->expensereport->error);
747 }
748
749 return $this->_cleanObjectDatas($this->expensereport);
750 }
751
752
773 public function deny($id, $details, $notrigger = 0)
774 {
775 if (!DolibarrApiAccess::$user->hasRight('expensereport', 'approve')) {
776 throw new RestException(403, "Insufficiant rights");
777 }
778 $result = $this->expensereport->fetch($id);
779 if (!$result) {
780 throw new RestException(404, 'Expense report not found');
781 }
782
783 if (!DolibarrApi::_checkAccessToResource('expensereport', $this->expensereport)) {
784 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
785 }
786
787 $result = $this->expensereport->setDeny(DolibarrApiAccess::$user, $details, $notrigger);
788 if ($result == 0) {
789 throw new RestException(304, 'Error nothing done. May be object is already denied');
790 }
791 if ($result < 0) {
792 throw new RestException(500, 'Error when denying expense report: '.$this->expensereport->error);
793 }
794
795
796
797 return $this->_cleanObjectDatas($this->expensereport);
798 }
799
817 public function cancel($id, $detail, $notrigger = 0)
818 {
819 if (!DolibarrApiAccess::$user->hasRight('expensereport', 'creer')) {
820 throw new RestException(403, "Insufficiant rights");
821 }
822 $result = $this->expensereport->fetch($id);
823 if (!$result) {
824 throw new RestException(404, 'Expense report not found');
825 }
826
827 if (!DolibarrApi::_checkAccessToResource('expensereport', $this->expensereport)) {
828 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
829 }
830
831 if ($this->expensereport->status == ExpenseReport::STATUS_CANCELED) {
832 throw new RestException(403, 'Expense report already canceled');
833 }
834 $result = $this->expensereport->set_cancel(DolibarrApiAccess::$user, $detail, $notrigger);
835 if ($result < 0) {
836 throw new RestException(500, 'Error when cancelling expense report: '.$this->expensereport->error);
837 }
838
839 $result = $this->expensereport->fetch($id);
840 return $this->_cleanObjectDatas($this->expensereport);
841 }
842
860 public function getAllPayments($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0)
861 {
862 $list = array();
863
864 if (!DolibarrApiAccess::$user->hasRight('expensereport', 'lire')) {
865 throw new RestException(403);
866 }
867
868 $sql = "SELECT t.rowid FROM " . MAIN_DB_PREFIX . "payment_expensereport as t, ".MAIN_DB_PREFIX."expensereport as e";
869 $sql .= " WHERE e.rowid = t.fk_expensereport";
870 $sql .= ' AND e.entity IN ('.getEntity('expensereport').')';
871
872 // Restrict to payments of expense reports the user is allowed to see
873 if (!DolibarrApiAccess::$user->hasRight('expensereport', 'readall')) {
874 $childids = DolibarrApiAccess::$user->getAllChildIds(1);
875 $sql .= " AND e.fk_user_author IN (".$this->db->sanitize(implode(',', $childids)).")";
876 }
877
878 $sql .= $this->db->order($sortfield, $sortorder);
879 if ($limit) {
880 if ($page < 0) {
881 $page = 0;
882 }
883 $offset = $limit * $page;
884
885 $sql .= $this->db->plimit($limit + 1, $offset);
886 }
887
888 dol_syslog("API Rest request");
889 $result = $this->db->query($sql);
890
891 if ($result) {
892 $num = $this->db->num_rows($result);
893 $min = min($num, ($limit <= 0 ? $num : $limit));
894 for ($i = 0; $i < $min; $i++) {
895 $obj = $this->db->fetch_object($result);
896 $paymentExpenseReport = new PaymentExpenseReport($this->db);
897 if ($paymentExpenseReport->fetch($obj->rowid) > 0) {
898 $list[] = $this->_cleanObjectDatas($paymentExpenseReport);
899 }
900 }
901 } else {
902 throw new RestException(503, 'Error when retrieving list of paymentexpensereport: ' . $this->db->lasterror());
903 }
904
905 return $list;
906 }
907
920 public function getPayments($pid)
921 {
922 if (!DolibarrApiAccess::$user->hasRight('expensereport', 'lire')) {
923 throw new RestException(403);
924 }
925
926 $paymentExpenseReport = new PaymentExpenseReport($this->db);
927 $result = $paymentExpenseReport->fetch($pid);
928 if (!$result) {
929 throw new RestException(404, 'paymentExpenseReport not found');
930 }
931
932 // Check access to the parent expense report
933 $result = $this->expensereport->fetch($paymentExpenseReport->fk_expensereport);
934 if (!$result) {
935 throw new RestException(404, 'Expense report not found');
936 }
937
938 if (!DolibarrApi::_checkAccessToResource('expensereport', $this->expensereport)) {
939 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
940 }
941
942 return $this->_cleanObjectDatas($paymentExpenseReport);
943 }
944
959 public function addPayment($id, $request_data = null)
960 {
961 if (!DolibarrApiAccess::$user->hasRight('expensereport', 'to_paid')) {
962 throw new RestException(403);
963 }
964 // Check mandatory fields
965 $result = $this->_validatepayment($request_data);
966
967 if ($this->expensereport->fetch($id) <= 0) {
968 throw new RestException(404, 'Expense report not found');
969 }
970 // The 'creer' right alone says nothing about whose report this is. expensereport/payment/payment.php
971 // gets the hierarchy check from restrictedArea(); the API must ask for it explicitly, like get() does.
972 if (!DolibarrApi::_checkAccessToResource('expensereport', $this->expensereport)) {
973 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
974 }
975 if (isModEnabled("bank") && !((int) ($request_data['accountid'] ?? 0) > 0)) {
976 throw new RestException(400, "accountid field missing");
977 }
978
979 $paymentExpenseReport = new PaymentExpenseReport($this->db);
980 $paymentExpenseReport->fk_expensereport = $id;
981 foreach ($request_data as $field => $value) {
982 $paymentExpenseReport->$field = $this->_checkValForAPI($field, $value, $paymentExpenseReport);
983 }
984
985 // Same checks as expensereport/payment/payment.php: only an approved report is paid, never more than the remainder
986 if ($this->expensereport->status != ExpenseReport::STATUS_APPROVED) {
987 throw new RestException(400, 'Expense report must be approved to be paid');
988 }
989 $totalpayment = 0;
990 foreach ((array) $paymentExpenseReport->amounts as $value) {
991 $totalpayment += (float) price2num($value, 'MT');
992 }
993 $remaintopay = (float) price2num($this->expensereport->total_ttc - $this->expensereport->getSumPayments(), 'MT');
994 if ((float) price2num($totalpayment, 'MT') > $remaintopay) {
995 throw new RestException(400, 'Payment higher than remainder to pay ('.$remaintopay.')');
996 }
997
998 // Same sequence as expensereport/payment/payment.php: all or nothing
999 $this->db->begin();
1000
1001 if ($paymentExpenseReport->create(DolibarrApiAccess::$user) < 0) {
1002 $this->db->rollback();
1003 throw new RestException(400, 'Payment error : '.$paymentExpenseReport->errorsToString());
1004 }
1005 if (isModEnabled("bank")) {
1006 $result = $paymentExpenseReport->addPaymentToBank(
1007 DolibarrApiAccess::$user,
1008 'payment_expensereport',
1009 '(ExpenseReportPayment)',
1010 (int) $request_data['accountid'],
1011 '',
1012 ''
1013 );
1014 if ($result <= 0) {
1015 $this->db->rollback();
1016 throw new RestException(400, 'Add payment to bank error : '.$paymentExpenseReport->errorsToString());
1017 }
1018 }
1019
1020 $remaintopay = price2num($this->expensereport->total_ttc - $this->expensereport->getSumPayments(), 'MT');
1021 if ($remaintopay == 0 && $this->expensereport->setPaid($this->expensereport->id, DolibarrApiAccess::$user) < 0) {
1022 $this->db->rollback();
1023 throw new RestException(400, 'Set paid error : '.$this->expensereport->errorsToString());
1024 }
1025
1026 $this->db->commit();
1027
1028 return $paymentExpenseReport->id;
1029 }
1030
1045 public function updatePayment($id, $request_data = null)
1046 {
1047 if (!DolibarrApiAccess::$user->hasRight('expensereport', 'to_paid')) {
1048 throw new RestException(403);
1049 }
1050
1051 $paymentExpenseReport = new PaymentExpenseReport($this->db);
1052 $result = $paymentExpenseReport->fetch($id);
1053 if (!$result) {
1054 throw new RestException(404, 'payment of expense report not found');
1055 }
1056
1057 // Check access to the parent expense report
1058 $result = $this->expensereport->fetch($paymentExpenseReport->fk_expensereport);
1059 if (!$result) {
1060 throw new RestException(404, 'Expense report not found');
1061 }
1062
1063 if (!DolibarrApi::_checkAccessToResource('expensereport', $this->expensereport)) {
1064 throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
1065 }
1066
1067 foreach ($request_data as $field => $value) {
1068 if ($field == 'id' || $field == 'fk_expensereport') { // A payment can't be moved to another report
1069 continue;
1070 }
1071 $paymentExpenseReport->$field = $this->_checkValForAPI($field, $value, $paymentExpenseReport);
1072 }
1073
1074 // The payments of the report can't exceed its total
1075 $oldpayment = new PaymentExpenseReport($this->db);
1076 $oldpayment->fetch($paymentExpenseReport->id);
1077 $remaintopay = (float) price2num($this->expensereport->total_ttc - $this->expensereport->getSumPayments() + (float) $oldpayment->amount, 'MT');
1078 if ((float) price2num($paymentExpenseReport->amount, 'MT') > $remaintopay) {
1079 throw new RestException(400, 'Payment higher than remainder to pay ('.$remaintopay.')');
1080 }
1081
1082 if ($paymentExpenseReport->update(DolibarrApiAccess::$user) > 0) {
1083 return $this->get($id);
1084 } else {
1085 throw new RestException(500, $paymentExpenseReport->error);
1086 }
1087 }
1088
1097 /*public function delete($id)
1098 {
1099 if (!DolibarrApiAccess::$user->hasRight('expensereport', 'creer') {
1100 throw new RestException(403);
1101 }
1102 $paymentExpenseReport = new PaymentExpenseReport($this->db);
1103 $result = $paymentExpenseReport->fetch($id);
1104 if (!$result) {
1105 throw new RestException(404, 'paymentExpenseReport not found');
1106 }
1107
1108 if ($paymentExpenseReport->delete(DolibarrApiAccess::$user) < 0) {
1109 throw new RestException(403, 'error when deleting paymentExpenseReport');
1110 }
1111
1112 return array(
1113 'success' => array(
1114 'code' => 200,
1115 'message' => 'paymentExpenseReport deleted'
1116 )
1117 );
1118 }*/
1119
1120
1121
1122 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
1132 protected function _cleanObjectDatas($object)
1133 {
1134 // phpcs:enable
1135 $object = parent::_cleanObjectDatas($object);
1136
1137 unset($object->fk_statut);
1138 unset($object->statut);
1139 unset($object->user);
1140 unset($object->thirdparty);
1141
1142 unset($object->cond_reglement);
1143 unset($object->shipping_method_id);
1144
1145 unset($object->barcode_type);
1146 unset($object->barcode_type_code);
1147 unset($object->barcode_type_label);
1148 unset($object->barcode_type_coder);
1149
1150 unset($object->code_paiement);
1151 unset($object->code_statut);
1152 unset($object->fk_c_paiement);
1153 unset($object->fk_incoterms);
1154 unset($object->label_incoterms);
1155 unset($object->location_incoterms);
1156 unset($object->mode_reglement_id);
1157 unset($object->cond_reglement_id);
1158
1159 unset($object->name);
1160 unset($object->lastname);
1161 unset($object->firstname);
1162 unset($object->civility_id);
1163 unset($object->cond_reglement_id);
1164 unset($object->contact);
1165 unset($object->contact_id);
1166
1167 unset($object->state);
1168 unset($object->state_id);
1169 unset($object->state_code);
1170 unset($object->country);
1171 unset($object->country_id);
1172 unset($object->country_code);
1173
1174 unset($object->note); // We already use note_public and note_pricate
1175
1176 return $object;
1177 }
1178
1186 private function _validate($data)
1187 {
1188 if ($data === null) {
1189 $data = array();
1190 }
1191 $expensereport = array();
1192 foreach (ExpenseReports::$FIELDS as $field) {
1193 if (!isset($data[$field])) {
1194 throw new RestException(400, "$field field missing");
1195 }
1196 $expensereport[$field] = $data[$field];
1197 }
1198 return $expensereport;
1199 }
1200
1208 private function _validatepayment($data)
1209 {
1210 if ($data === null) {
1211 $data = array();
1212 }
1213 $expensereport = array();
1214 foreach (ExpenseReports::$FIELDSPAYMENT as $field) {
1215 if (!isset($data[$field])) {
1216 throw new RestException(400, "$field field missing");
1217 }
1218 $expensereport[$field] = $data[$field];
1219 }
1220 return $expensereport;
1221 }
1222
1231 private function _validateLine($data)
1232 {
1233 if ($data === null) {
1234 $data = array();
1235 }
1236 $expenseReport = array();
1237 foreach (ExpenseReports::$FIELDSLINE as $field) {
1238 if (!isset($data[$field])) {
1239 throw new RestException(400, "$field field missing");
1240 }
1241 $expenseReport[$field] = $data[$field];
1242 }
1243 return $expenseReport;
1244 }
1245}
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:47
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
Class for API REST v1.
Definition api.class.php:33
_filterObjectProperties($object, $properties)
Filter properties that will be returned on object.
static _checkAccessToResource($resource, $resource_id=0, $dbtablename='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid')
Check access by user to a given resource.
_checkValForAPI($field, $value, $object)
Check and convert a string depending on its type/name.
Definition api.class.php:98
Class to manage Trips and Expenses.
const STATUS_DRAFT
Draft status.
const STATUS_APPROVED
Classified approved.
const STATUS_CANCELED
Classified canceled.
const STATUS_REFUSED
Classified refused.
Class of expense report details lines.
getPayments($pid)
Get an expense report payment.
deny($id, $details, $notrigger=0)
Deny an expense report.
_cleanObjectDatas($object)
Delete paymentExpenseReport.
_validate($data)
Validate fields before create or update object.
validate($id, $notrigger=0)
Validate an expense report.
deleteLine($id, $lineid)
Delete a line from an expense report.
getLines($id)
Get lines of an expense report.
updatePayment($id, $request_data=null)
Update a payment of an expense report.
approve($id, $notrigger=0)
Approve an expense report.
put($id, $request_data=null)
Update expense report general fields.
cancel($id, $detail, $notrigger=0)
Cancel an expense report.
addPayment($id, $request_data=null)
Create a payment for an expense report.
getAllPayments($sortfield="t.rowid", $sortorder='ASC', $limit=100, $page=0)
Get the list of payments of an expense report.
post($request_data=null)
Create an expense report.
_validatepayment($data)
Validate fields before create or update object.
_validateLine($data)
Validate fields before create or update object.
setToDraft($id)
Set an expense report to draft.
putLine($id, $lineid, $request_data=null)
Update a line of an expense report.
index($sortfield="t.rowid", $sortorder='ASC', $limit=100, $page=0, $user_ids='', $sqlfilters='', $properties='', $pagination_data=false)
List expense reports.
postLine($id, $request_data=null)
Add a line to an expense report.
Class to manage payments of expense report.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0)
forgeSQLFromUniversalSearchCriteria
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
sanitizeVal($out='', $check='alphanohtml', $filter=null, $options=null)
Return a sanitized or empty value after checking value against a rule.
isModEnabled($module)
Is Dolibarr module enabled.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.