dolibarr 21.0.0-alpha
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 $account->$field = $this->_checkValForAPI($field, $value, $account);
352 }
353
354 if ($account->update(DolibarrApiAccess::$user) > 0) {
355 return $this->get($id);
356 } else {
357 throw new RestException(500, $account->error);
358 }
359 }
360
367 public function delete($id)
368 {
369 if (!DolibarrApiAccess::$user->hasRight('banque', 'configurer')) {
370 throw new RestException(403);
371 }
372 $account = new Account($this->db);
373 $result = $account->fetch($id);
374 if (!$result) {
375 throw new RestException(404, 'account not found');
376 }
377
378 if ($account->delete(DolibarrApiAccess::$user) < 0) {
379 throw new RestException(500, 'error when deleting account');
380 }
381
382 return array(
383 'success' => array(
384 'code' => 200,
385 'message' => 'account deleted'
386 )
387 );
388 }
389
398 private function _validate($data)
399 {
400 $account = array();
401 foreach (BankAccounts::$FIELDS as $field) {
402 if (!isset($data[$field])) {
403 throw new RestException(400, "$field field missing");
404 }
405 $account[$field] = $data[$field];
406 }
407 return $account;
408 }
409
410 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
417 protected function _cleanObjectDatas($object)
418 {
419 // phpcs:enable
420 $object = parent::_cleanObjectDatas($object);
421
422 unset($object->rowid);
423
424 return $object;
425 }
426
438 public function getLines($id, $sqlfilters = '')
439 {
440 $list = array();
441
442 if (!DolibarrApiAccess::$user->hasRight('banque', 'lire')) {
443 throw new RestException(403);
444 }
445
446 $account = new Account($this->db);
447 $result = $account->fetch($id);
448 if (!$result) {
449 throw new RestException(404, 'account not found');
450 }
451
452 $sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . "bank ";
453 $sql .= " WHERE fk_account = " . ((int) $id);
454
455 // Add sql filters
456 if ($sqlfilters) {
457 $errormessage = '';
458 $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
459 if ($errormessage) {
460 throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage);
461 }
462 }
463
464 $sql .= " ORDER BY rowid";
465
466 $result = $this->db->query($sql);
467
468 if ($result) {
469 $num = $this->db->num_rows($result);
470 for ($i = 0; $i < $num; $i++) {
471 $obj = $this->db->fetch_object($result);
472 $accountLine = new AccountLine($this->db);
473 if ($accountLine->fetch($obj->rowid) > 0) {
474 $list[] = $this->_cleanObjectDatas($accountLine);
475 }
476 }
477 } else {
478 throw new RestException(503, 'Error when retrieving list of account lines: ' . $this->db->lasterror());
479 }
480
481 return $list;
482 }
483
503 public function addLine($id, $date, $type, $label, $amount, $category = 0, $cheque_number = '', $cheque_writer = '', $cheque_bank = '', $accountancycode = '', $datev = null, $num_releve = '')
504 {
505 if (!DolibarrApiAccess::$user->hasRight('banque', 'modifier')) {
506 throw new RestException(403);
507 }
508
509 $account = new Account($this->db);
510 $result = $account->fetch($id);
511 if (!$result) {
512 throw new RestException(404, 'account not found');
513 }
514
515 $type = sanitizeVal($type);
516 $label = sanitizeVal($label);
517 $cheque_number = sanitizeVal($cheque_number);
518 $cheque_writer = sanitizeVal($cheque_writer);
519 $cheque_bank = sanitizeVal($cheque_bank);
520 $accountancycode = sanitizeVal($accountancycode);
521 $num_releve = sanitizeVal($num_releve);
522
523 $result = $account->addline(
524 $date,
525 $type,
526 $label,
527 $amount,
528 $cheque_number,
529 $category,
530 DolibarrApiAccess::$user,
531 $cheque_writer,
532 $cheque_bank,
533 $accountancycode,
534 $datev,
535 $num_releve
536 );
537 if ($result < 0) {
538 throw new RestException(503, 'Error when adding line to account: ' . $account->error);
539 }
540 return $result;
541 }
542
556 public function addLink($id, $line_id, $url_id, $url, $label, $type)
557 {
558 if (!DolibarrApiAccess::$user->hasRight('banque', 'modifier')) {
559 throw new RestException(403);
560 }
561
562 $account = new Account($this->db);
563 $result = $account->fetch($id);
564 if (!$result) {
565 throw new RestException(404, 'account not found');
566 }
567
568 $accountLine = new AccountLine($this->db);
569 $result = $accountLine->fetch($line_id);
570 if (!$result) {
571 throw new RestException(404, 'account line not found');
572 }
573
574 $url = sanitizeVal($url);
575 $label = sanitizeVal($label);
576 $type = sanitizeVal($type);
577
578 $result = $account->add_url_line($line_id, $url_id, $url, $label, $type);
579 if ($result < 0) {
580 throw new RestException(503, 'Error when adding link to account line: ' . $account->error);
581 }
582 return $result;
583 }
584
597 public function getLinks($id, $line_id)
598 {
599 $list = array();
600
601 if (!DolibarrApiAccess::$user->hasRight('banque', 'lire')) {
602 throw new RestException(403);
603 }
604
605 $account = new Account($this->db);
606 $result = $account->fetch($id);
607 if (!$result) {
608 throw new RestException(404, 'account not found');
609 }
610
611 $links = $account->get_url($line_id); // Get an array('url'=>, 'url_id'=>, 'label'=>, 'type'=> 'fk_bank'=> )
612 foreach ($links as &$link) {
613 unset($link[0], $link[1], $link[2], $link[3]); // Remove the numeric keys
614 }
615
616 return $links;
617 }
618
629 public function updateLine($id, $line_id, $label)
630 {
631 if (!DolibarrApiAccess::$user->rights->banque->modifier) {
632 throw new RestException(403);
633 }
634
635 $account = new Account($this->db);
636 $result = $account->fetch($id);
637 if (!$result) {
638 throw new RestException(404, 'account not found');
639 }
640
641 $accountLine = new AccountLine($this->db);
642 $result = $accountLine->fetch($line_id);
643 if (!$result) {
644 throw new RestException(404, 'account line not found');
645 }
646
647 $accountLine->label = sanitizeVal($label);
648
649 $result = $accountLine->updateLabel();
650 if ($result < 0) {
651 throw new RestException(503, 'Error when updating link to account line: ' . $accountLine->error);
652 }
653 return $accountLine->id;
654 }
655
665 public function deleteLine($id, $line_id)
666 {
667 if (!DolibarrApiAccess::$user->rights->banque->modifier) {
668 throw new RestException(403);
669 }
670
671 $account = new Account($this->db);
672 $result = $account->fetch($id);
673 if (!$result) {
674 throw new RestException(404, 'account not found');
675 }
676
677 $accountLine = new AccountLine($this->db);
678 $result = $accountLine->fetch($line_id);
679 if (!$result) {
680 throw new RestException(404, 'account line not found');
681 }
682
683 if ($accountLine->delete(DolibarrApiAccess::$user) < 0) {
684 throw new RestException(500, 'error when deleting account line');
685 }
686
687 return array(
688 'success' => array(
689 'code' => 200,
690 'message' => "account line $line_id deleted"
691 )
692 );
693 }
694}
$id
Definition account.php:39
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.