dolibarr 20.0.5
api_bankaccounts.class.php
1<?php
2/*
3 * Copyright (C) 2016 Xebax Christy <xebax@wanadoo.fr>
4 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
5 * Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21use Luracast\Restler\RestException;
22
23require_once DOL_DOCUMENT_ROOT . '/compta/bank/class/account.class.php';
24
33{
37 public static $FIELDS = array(
38 'ref',
39 'label',
40 'type',
41 'currency_code',
42 'country_id'
43 );
44
48 public function __construct()
49 {
50 global $db;
51 $this->db = $db;
52 }
53
68 public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $category = 0, $sqlfilters = '', $properties = '')
69 {
70 $list = array();
71
72 if (!DolibarrApiAccess::$user->hasRight('banque', 'lire')) {
73 throw new RestException(403);
74 }
75
76 $sql = "SELECT t.rowid FROM ".MAIN_DB_PREFIX."bank_account AS t LEFT JOIN ".MAIN_DB_PREFIX."bank_account_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
77 if ($category > 0) {
78 $sql .= ", " . MAIN_DB_PREFIX . "categorie_account as c";
79 }
80 $sql .= ' WHERE t.entity IN (' . getEntity('bank_account') . ')';
81 // Select accounts of given category
82 if ($category > 0) {
83 $sql .= " AND c.fk_categorie = " . ((int) $category) . " AND c.fk_account = t.rowid";
84 }
85 // Add sql filters
86 if ($sqlfilters) {
87 $errormessage = '';
88 $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
89 if ($errormessage) {
90 throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage);
91 }
92 }
93
94 $sql .= $this->db->order($sortfield, $sortorder);
95 if ($limit) {
96 if ($page < 0) {
97 $page = 0;
98 }
99 $offset = $limit * $page;
100
101 $sql .= $this->db->plimit($limit + 1, $offset);
102 }
103
104 dol_syslog("API Rest request");
105 $result = $this->db->query($sql);
106
107 if ($result) {
108 $num = $this->db->num_rows($result);
109 $min = min($num, ($limit <= 0 ? $num : $limit));
110 for ($i = 0; $i < $min; $i++) {
111 $obj = $this->db->fetch_object($result);
112 $account = new Account($this->db);
113 if ($account->fetch($obj->rowid) > 0) {
114 $list[] = $this->_filterObjectProperties($this->_cleanObjectDatas($account), $properties);
115 }
116 }
117 } else {
118 throw new RestException(503, 'Error when retrieving list of accounts: ' . $this->db->lasterror());
119 }
120
121 return $list;
122 }
123
132 public function get($id)
133 {
134 if (!DolibarrApiAccess::$user->hasRight('banque', 'lire')) {
135 throw new RestException(403);
136 }
137
138 $account = new Account($this->db);
139 $result = $account->fetch($id);
140 if (!$result) {
141 throw new RestException(404, 'account not found');
142 }
143
144 return $this->_cleanObjectDatas($account);
145 }
146
153 public function post($request_data = null)
154 {
155 if (!DolibarrApiAccess::$user->hasRight('banque', 'configurer')) {
156 throw new RestException(403);
157 }
158 // Check mandatory fields
159 $result = $this->_validate($request_data);
160
161 $account = new Account($this->db);
162 // Date of the initial balance (required to create an account).
163 $account->date_solde = time();
164 foreach ($request_data as $field => $value) {
165 if ($field === 'caller') {
166 // 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
167 $account->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
168 continue;
169 }
170
171 $account->$field = $this->_checkValForAPI($field, $value, $account);
172 }
173 // courant and type are the same thing but the one used when
174 // creating an account is courant
175 $account->courant = $account->type; // deprecated
176
177 if ($account->create(DolibarrApiAccess::$user) < 0) {
178 throw new RestException(500, 'Error creating bank account', array_merge(array($account->error), $account->errors));
179 }
180 return $account->id;
181 }
182
205 public function transfer($bankaccount_from_id = 0, $bankaccount_to_id = 0, $date = null, $description = "", $amount = 0.0, $amount_to = 0.0, $cheque_number = "")
206 {
207 if (!DolibarrApiAccess::$user->hasRight('banque', 'configurer')) {
208 throw new RestException(403);
209 }
210
211 require_once DOL_DOCUMENT_ROOT . '/compta/bank/class/account.class.php';
212
213 $accountfrom = new Account($this->db);
214 $resultAccountFrom = $accountfrom->fetch($bankaccount_from_id);
215
216 if ($resultAccountFrom === 0) {
217 throw new RestException(404, 'The BankAccount for bankaccount_from_id provided does not exist.');
218 }
219
220 $accountto = new Account($this->db);
221 $resultAccountTo = $accountto->fetch($bankaccount_to_id);
222
223 if ($resultAccountTo === 0) {
224 throw new RestException(404, 'The BankAccount for bankaccount_to_id provided does not exist.');
225 }
226
227 if ($accountto->currency_code == $accountfrom->currency_code) {
228 $amount_to = $amount;
229 } else {
230 if (!$amount_to || empty($amount_to)) {
231 throw new RestException(422, 'You must provide amount_to value since bankaccount_from and bankaccount_to does not share the same currency.');
232 }
233 }
234
235 if ($amount_to < 0) {
236 throw new RestException(422, 'You must provide a positive value for amount.');
237 }
238
239 if ($accountto->id == $accountfrom->id) {
240 throw new RestException(422, 'bankaccount_from_id and bankaccount_to_id must be different !');
241 }
242
243 $this->db->begin();
244
245 $error = 0;
246 $bank_line_id_from = 0;
247 $bank_line_id_to = 0;
248 $result = 0;
249 $user = DolibarrApiAccess::$user;
250
251 // By default, electronic transfer from bank to bank
252 $typefrom = 'PRE';
253 $typeto = 'VIR';
254
255 if ($accountto->type == Account::TYPE_CASH || $accountfrom->type == Account::TYPE_CASH) {
256 // This is transfer of change
257 $typefrom = 'LIQ';
258 $typeto = 'LIQ';
259 }
260
261 // Clean data
262 $description = sanitizeVal($description, 'alphanohtml');
263 $cheque_number = sanitizeVal($cheque_number, 'alphanohtml');
264
269 if (!$error) {
270 $bank_line_id_from = $accountfrom->addline($date, $typefrom, $description, -1 * (float) price2num($amount), '', '', $user, $cheque_number);
271 }
272 if (!($bank_line_id_from > 0)) {
273 $error++;
274 }
275
276 if (!$error) {
277 $bank_line_id_to = $accountto->addline($date, $typeto, $description, price2num($amount_to), '', '', $user, $cheque_number);
278 }
279 if (!($bank_line_id_to > 0)) {
280 $error++;
281 }
282
287 $url = DOL_URL_ROOT . '/compta/bank/line.php?rowid=';
288 $label = '(banktransfert)';
289 $type = 'banktransfert';
290
291 if (!$error) {
292 $result = $accountfrom->add_url_line($bank_line_id_from, $bank_line_id_to, $url, $label, $type);
293 }
294 if (!($result > 0)) {
295 $error++;
296 }
297
298 if (!$error) {
299 $result = $accountto->add_url_line($bank_line_id_to, $bank_line_id_from, $url, $label, $type);
300 }
301 if (!($result > 0)) {
302 $error++;
303 }
304
305 if (!$error) {
306 $this->db->commit();
307
308 return array(
309 'success' => array(
310 'code' => 201,
311 'message' => 'Internal wire transfer created successfully.',
312 'bank_id_from' => $bank_line_id_from,
313 'bank_id_to' => $bank_line_id_to,
314 )
315 );
316 } else {
317 $this->db->rollback();
318 throw new RestException(500, $accountfrom->error . ' ' . $accountto->error);
319 }
320 }
321
329 public function put($id, $request_data = null)
330 {
331 if (!DolibarrApiAccess::$user->hasRight('banque', 'configurer')) {
332 throw new RestException(403);
333 }
334
335 $account = new Account($this->db);
336 $result = $account->fetch($id);
337 if (!$result) {
338 throw new RestException(404, 'account not found');
339 }
340
341 foreach ($request_data as $field => $value) {
342 if ($field == 'id') {
343 continue;
344 }
345 if ($field === 'caller') {
346 // 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
347 $account->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
348 continue;
349 }
350
351 if ($field == 'array_options' && is_array($value)) {
352 foreach ($value as $index => $val) {
353 $account->array_options[$index] = $this->_checkValForAPI($field, $val, $account);
354 }
355 continue;
356 }
357 $account->$field = $this->_checkValForAPI($field, $value, $account);
358 }
359
360 if ($account->update(DolibarrApiAccess::$user) > 0) {
361 return $this->get($id);
362 } else {
363 throw new RestException(500, $account->error);
364 }
365 }
366
373 public function delete($id)
374 {
375 if (!DolibarrApiAccess::$user->hasRight('banque', 'configurer')) {
376 throw new RestException(403);
377 }
378 $account = new Account($this->db);
379 $result = $account->fetch($id);
380 if (!$result) {
381 throw new RestException(404, 'account not found');
382 }
383
384 if ($account->delete(DolibarrApiAccess::$user) < 0) {
385 throw new RestException(500, 'error when deleting account');
386 }
387
388 return array(
389 'success' => array(
390 'code' => 200,
391 'message' => 'account deleted'
392 )
393 );
394 }
395
404 private function _validate($data)
405 {
406 $account = array();
407 foreach (BankAccounts::$FIELDS as $field) {
408 if (!isset($data[$field])) {
409 throw new RestException(400, "$field field missing");
410 }
411 $account[$field] = $data[$field];
412 }
413 return $account;
414 }
415
416 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
423 protected function _cleanObjectDatas($object)
424 {
425 // phpcs:enable
426 $object = parent::_cleanObjectDatas($object);
427
428 unset($object->rowid);
429
430 return $object;
431 }
432
444 public function getLines($id, $sqlfilters = '')
445 {
446 $list = array();
447
448 if (!DolibarrApiAccess::$user->hasRight('banque', 'lire')) {
449 throw new RestException(403);
450 }
451
452 $account = new Account($this->db);
453 $result = $account->fetch($id);
454 if (!$result) {
455 throw new RestException(404, 'account not found');
456 }
457
458 $sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . "bank ";
459 $sql .= " WHERE fk_account = " . ((int) $id);
460
461 // Add sql filters
462 if ($sqlfilters) {
463 $errormessage = '';
464 $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
465 if ($errormessage) {
466 throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage);
467 }
468 }
469
470 $sql .= " ORDER BY rowid";
471
472 $result = $this->db->query($sql);
473
474 if ($result) {
475 $num = $this->db->num_rows($result);
476 for ($i = 0; $i < $num; $i++) {
477 $obj = $this->db->fetch_object($result);
478 $accountLine = new AccountLine($this->db);
479 if ($accountLine->fetch($obj->rowid) > 0) {
480 $list[] = $this->_cleanObjectDatas($accountLine);
481 }
482 }
483 } else {
484 throw new RestException(503, 'Error when retrieving list of account lines: ' . $this->db->lasterror());
485 }
486
487 return $list;
488 }
489
509 public function addLine($id, $date, $type, $label, $amount, $category = 0, $cheque_number = '', $cheque_writer = '', $cheque_bank = '', $accountancycode = '', $datev = null, $num_releve = '')
510 {
511 if (!DolibarrApiAccess::$user->hasRight('banque', 'modifier')) {
512 throw new RestException(403);
513 }
514
515 $account = new Account($this->db);
516 $result = $account->fetch($id);
517 if (!$result) {
518 throw new RestException(404, 'account not found');
519 }
520
521 $type = sanitizeVal($type);
522 $label = sanitizeVal($label);
523 $cheque_number = sanitizeVal($cheque_number);
524 $cheque_writer = sanitizeVal($cheque_writer);
525 $cheque_bank = sanitizeVal($cheque_bank);
526 $accountancycode = sanitizeVal($accountancycode);
527 $num_releve = sanitizeVal($num_releve);
528
529 $result = $account->addline(
530 $date,
531 $type,
532 $label,
533 $amount,
534 $cheque_number,
535 $category,
536 DolibarrApiAccess::$user,
537 $cheque_writer,
538 $cheque_bank,
539 $accountancycode,
540 $datev,
541 $num_releve
542 );
543 if ($result < 0) {
544 throw new RestException(503, 'Error when adding line to account: ' . $account->error);
545 }
546 return $result;
547 }
548
562 public function addLink($id, $line_id, $url_id, $url, $label, $type)
563 {
564 if (!DolibarrApiAccess::$user->hasRight('banque', 'modifier')) {
565 throw new RestException(403);
566 }
567
568 $account = new Account($this->db);
569 $result = $account->fetch($id);
570 if (!$result) {
571 throw new RestException(404, 'account not found');
572 }
573
574 $accountLine = new AccountLine($this->db);
575 $result = $accountLine->fetch($line_id);
576 if (!$result) {
577 throw new RestException(404, 'account line not found');
578 }
579
580 $url = sanitizeVal($url);
581 $label = sanitizeVal($label);
582 $type = sanitizeVal($type);
583
584 $result = $account->add_url_line($line_id, $url_id, $url, $label, $type);
585 if ($result < 0) {
586 throw new RestException(503, 'Error when adding link to account line: ' . $account->error);
587 }
588 return $result;
589 }
590
603 public function getLinks($id, $line_id)
604 {
605 $list = array();
606
607 if (!DolibarrApiAccess::$user->hasRight('banque', 'lire')) {
608 throw new RestException(403);
609 }
610
611 $account = new Account($this->db);
612 $result = $account->fetch($id);
613 if (!$result) {
614 throw new RestException(404, 'account not found');
615 }
616
617 $links = $account->get_url($line_id); // Get an array('url'=>, 'url_id'=>, 'label'=>, 'type'=> 'fk_bank'=> )
618 foreach ($links as &$link) {
619 unset($link[0], $link[1], $link[2], $link[3]); // Remove the numeric keys
620 }
621
622 return $links;
623 }
624
635 public function updateLine($id, $line_id, $label)
636 {
637 if (!DolibarrApiAccess::$user->rights->banque->modifier) {
638 throw new RestException(403);
639 }
640
641 $account = new Account($this->db);
642 $result = $account->fetch($id);
643 if (!$result) {
644 throw new RestException(404, 'account not found');
645 }
646
647 $accountLine = new AccountLine($this->db);
648 $result = $accountLine->fetch($line_id);
649 if (!$result) {
650 throw new RestException(404, 'account line not found');
651 }
652
653 $accountLine->label = sanitizeVal($label);
654
655 $result = $accountLine->updateLabel();
656 if ($result < 0) {
657 throw new RestException(503, 'Error when updating link to account line: ' . $accountLine->error);
658 }
659 return $accountLine->id;
660 }
661
671 public function deleteLine($id, $line_id)
672 {
673 if (!DolibarrApiAccess::$user->rights->banque->modifier) {
674 throw new RestException(403);
675 }
676
677 $account = new Account($this->db);
678 $result = $account->fetch($id);
679 if (!$result) {
680 throw new RestException(404, 'account not found');
681 }
682
683 $accountLine = new AccountLine($this->db);
684 $result = $accountLine->fetch($line_id);
685 if (!$result) {
686 throw new RestException(404, 'account line not found');
687 }
688
689 if ($accountLine->delete(DolibarrApiAccess::$user) < 0) {
690 throw new RestException(500, 'error when deleting account line');
691 }
692
693 return array(
694 'success' => array(
695 'code' => 200,
696 'message' => "account line $line_id deleted"
697 )
698 );
699 }
700}
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:58
Class to manage bank accounts.
const TYPE_CASH
Cash account.
Class to manage bank transaction lines.
post($request_data=null)
Create account object.
addLink($id, $line_id, $url_id, $url, $label, $type)
Add a link to an account line.
put($id, $request_data=null)
Update account.
_validate($data)
Validate fields before creating an object.
getLinks($id, $line_id)
Get the list of links for a line of the account.
transfer($bankaccount_from_id=0, $bankaccount_to_id=0, $date=null, $description="", $amount=0.0, $amount_to=0.0, $cheque_number="")
Create an internal wire transfer between two bank accounts.
__construct()
Constructor.
addLine($id, $date, $type, $label, $amount, $category=0, $cheque_number='', $cheque_writer='', $cheque_bank='', $accountancycode='', $datev=null, $num_releve='')
Add a line to an account.
index($sortfield="t.rowid", $sortorder='ASC', $limit=100, $page=0, $category=0, $sqlfilters='', $properties='')
Get the list of accounts.
getLines($id, $sqlfilters='')
Get the list of lines of the account.
deleteLine($id, $line_id)
Delete an account line.
static $FIELDS
array $FIELDS Mandatory fields, checked when creating an object
_cleanObjectDatas($object)
Clean sensible object datas.
updateLine($id, $line_id, $label)
Update an account line.
Class for API REST v1.
Definition api.class.php:30
_filterObjectProperties($object, $properties)
Filter properties that will be returned on object.
_checkValForAPI($field, $value, $object)
Check and convert a string depending on its type/name.
Definition api.class.php:82
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
sanitizeVal($out='', $check='alphanohtml', $filter=null, $options=null)
Return a sanitized or empty value after checking value against a rule.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.