dolibarr 19.0.3
api_bankaccounts.class.php
1<?php
2/*
3 * Copyright (C) 2016 Xebax Christy <xebax@wanadoo.fr>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19use Luracast\Restler\RestException;
20
21require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
22
31{
35 public static $FIELDS = array(
36 'ref',
37 'label',
38 'type',
39 'currency_code',
40 'country_id'
41 );
42
46 public function __construct()
47 {
48 global $db;
49 $this->db = $db;
50 }
51
66 public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $category = 0, $sqlfilters = '', $properties = '')
67 {
68 $list = array();
69
70 if (!DolibarrApiAccess::$user->rights->banque->lire) {
71 throw new RestException(401);
72 }
73
74 $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
75 if ($category > 0) {
76 $sql .= ", ".MAIN_DB_PREFIX."categorie_account as c";
77 }
78 $sql .= ' WHERE t.entity IN ('.getEntity('bank_account').')';
79 // Select accounts of given category
80 if ($category > 0) {
81 $sql .= " AND c.fk_categorie = ".((int) $category)." AND c.fk_account = t.rowid";
82 }
83 // Add sql filters
84 if ($sqlfilters) {
85 $errormessage = '';
86 $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
87 if ($errormessage) {
88 throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage);
89 }
90 }
91
92 $sql .= $this->db->order($sortfield, $sortorder);
93 if ($limit) {
94 if ($page < 0) {
95 $page = 0;
96 }
97 $offset = $limit * $page;
98
99 $sql .= $this->db->plimit($limit + 1, $offset);
100 }
101
102 dol_syslog("API Rest request");
103 $result = $this->db->query($sql);
104
105 if ($result) {
106 $num = $this->db->num_rows($result);
107 $min = min($num, ($limit <= 0 ? $num : $limit));
108 for ($i = 0; $i < $min; $i++) {
109 $obj = $this->db->fetch_object($result);
110 $account = new Account($this->db);
111 if ($account->fetch($obj->rowid) > 0) {
112 $list[] = $this->_filterObjectProperties($this->_cleanObjectDatas($account), $properties);
113 }
114 }
115 } else {
116 throw new RestException(503, 'Error when retrieving list of accounts: '.$this->db->lasterror());
117 }
118
119 return $list;
120 }
121
130 public function get($id)
131 {
132 if (!DolibarrApiAccess::$user->rights->banque->lire) {
133 throw new RestException(401);
134 }
135
136 $account = new Account($this->db);
137 $result = $account->fetch($id);
138 if (!$result) {
139 throw new RestException(404, 'account not found');
140 }
141
142 return $this->_cleanObjectDatas($account);
143 }
144
151 public function post($request_data = null)
152 {
153 if (!DolibarrApiAccess::$user->rights->banque->configurer) {
154 throw new RestException(401);
155 }
156 // Check mandatory fields
157 $result = $this->_validate($request_data);
158
159 $account = new Account($this->db);
160 foreach ($request_data as $field => $value) {
161 if ($field === 'caller') {
162 // 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 whith the caller
163 $account->context['caller'] = $request_data['caller'];
164 continue;
165 }
166
167 $account->$field = $this->_checkValForAPI($field, $value, $account);
168 }
169 // Date of the initial balance (required to create an account).
170 $account->date_solde = time();
171 // courant and type are the same thing but the one used when
172 // creating an account is courant
173 $account->courant = $account->type;
174
175 if ($account->create(DolibarrApiAccess::$user) < 0) {
176 throw new RestException(500, 'Error creating bank account', array_merge(array($account->error), $account->errors));
177 }
178 return $account->id;
179 }
180
202 public function transfer($bankaccount_from_id = 0, $bankaccount_to_id = 0, $date = null, $description = "", $amount = 0.0, $amount_to = 0.0)
203 {
204 if (!DolibarrApiAccess::$user->rights->banque->configurer) {
205 throw new RestException(401);
206 }
207
208 require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
209
210 $accountfrom = new Account($this->db);
211 $resultAccountFrom = $accountfrom->fetch($bankaccount_from_id);
212
213 if ($resultAccountFrom === 0) {
214 throw new RestException(404, 'The BankAccount for bankaccount_from_id provided does not exist.');
215 }
216
217 $accountto = new Account($this->db);
218 $resultAccountTo = $accountto->fetch($bankaccount_to_id);
219
220 if ($resultAccountTo === 0) {
221 throw new RestException(404, 'The BankAccount for bankaccount_to_id provided does not exist.');
222 }
223
224 if ($accountto->currency_code == $accountfrom->currency_code) {
225 $amount_to = $amount;
226 } else {
227 if (!$amount_to || empty($amount_to)) {
228 throw new RestException(422, 'You must provide amount_to value since bankaccount_from and bankaccount_to does not share the same currency.');
229 }
230 }
231
232 if ($amount_to < 0) {
233 throw new RestException(422, 'You must provide a positive value for amount.');
234 }
235
236 if ($accountto->id == $accountfrom->id) {
237 throw new RestException(422, 'bankaccount_from_id and bankaccount_to_id must be different !');
238 }
239
240 $this->db->begin();
241
242 $error = 0;
243 $bank_line_id_from = 0;
244 $bank_line_id_to = 0;
245 $result = 0;
246 $user = DolibarrApiAccess::$user;
247
248 // By default, electronic transfert from bank to bank
249 $typefrom = 'PRE';
250 $typeto = 'VIR';
251
252 if ($accountto->courant == Account::TYPE_CASH || $accountfrom->courant == Account::TYPE_CASH) {
253 // This is transfer of change
254 $typefrom = 'LIQ';
255 $typeto = 'LIQ';
256 }
257
258 // Clean data
259 $description = sanitizeVal($description, 'alphanohtml');
260
261
266 if (!$error) {
267 $bank_line_id_from = $accountfrom->addline($date, $typefrom, $description, -1 * price2num($amount), '', '', $user);
268 }
269 if (!($bank_line_id_from > 0)) {
270 $error++;
271 }
272
273 if (!$error) {
274 $bank_line_id_to = $accountto->addline($date, $typeto, $description, price2num($amount_to), '', '', $user);
275 }
276 if (!($bank_line_id_to > 0)) {
277 $error++;
278 }
279
284 $url = DOL_URL_ROOT.'/compta/bank/line.php?rowid=';
285 $label = '(banktransfert)';
286 $type = 'banktransfert';
287
288 if (!$error) {
289 $result = $accountfrom->add_url_line($bank_line_id_from, $bank_line_id_to, $url, $label, $type);
290 }
291 if (!($result > 0)) {
292 $error++;
293 }
294
295 if (!$error) {
296 $result = $accountto->add_url_line($bank_line_id_to, $bank_line_id_from, $url, $label, $type);
297 }
298 if (!($result > 0)) {
299 $error++;
300 }
301
302 if (!$error) {
303 $this->db->commit();
304
305 return array(
306 'success' => array(
307 'code' => 201,
308 'message' => 'Internal wire transfer created successfully.',
309 'bank_id_from' => $bank_line_id_from,
310 'bank_id_to' => $bank_line_id_to,
311 )
312 );
313 } else {
314 $this->db->rollback();
315 throw new RestException(500, $accountfrom->error.' '.$accountto->error);
316 }
317 }
318
326 public function put($id, $request_data = null)
327 {
328 if (!DolibarrApiAccess::$user->rights->banque->configurer) {
329 throw new RestException(401);
330 }
331
332 $account = new Account($this->db);
333 $result = $account->fetch($id);
334 if (!$result) {
335 throw new RestException(404, 'account not found');
336 }
337
338 foreach ($request_data as $field => $value) {
339 if ($field == 'id') {
340 continue;
341 }
342 if ($field === 'caller') {
343 // 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 whith the caller
344 $account->context['caller'] = $request_data['caller'];
345 continue;
346 }
347
348 $account->$field = $this->_checkValForAPI($field, $value, $account);
349 }
350
351 if ($account->update(DolibarrApiAccess::$user) > 0) {
352 return $this->get($id);
353 } else {
354 throw new RestException(500, $account->error);
355 }
356 }
357
364 public function delete($id)
365 {
366 if (!DolibarrApiAccess::$user->rights->banque->configurer) {
367 throw new RestException(401);
368 }
369 $account = new Account($this->db);
370 $result = $account->fetch($id);
371 if (!$result) {
372 throw new RestException(404, 'account not found');
373 }
374
375 if ($account->delete(DolibarrApiAccess::$user) < 0) {
376 throw new RestException(401, 'error when deleting account');
377 }
378
379 return array(
380 'success' => array(
381 'code' => 200,
382 'message' => 'account deleted'
383 )
384 );
385 }
386
395 private function _validate($data)
396 {
397 $account = array();
398 foreach (BankAccounts::$FIELDS as $field) {
399 if (!isset($data[$field])) {
400 throw new RestException(400, "$field field missing");
401 }
402 $account[$field] = $data[$field];
403 }
404 return $account;
405 }
406
407 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
414 protected function _cleanObjectDatas($object)
415 {
416 // phpcs:enable
417 $object = parent::_cleanObjectDatas($object);
418
419 unset($object->rowid);
420
421 return $object;
422 }
423
435 public function getLines($id, $sqlfilters = '')
436 {
437 $list = array();
438
439 if (!DolibarrApiAccess::$user->rights->banque->lire) {
440 throw new RestException(401);
441 }
442
443 $account = new Account($this->db);
444 $result = $account->fetch($id);
445 if (!$result) {
446 throw new RestException(404, 'account not found');
447 }
448
449 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."bank ";
450 $sql .= " WHERE fk_account = ".((int) $id);
451
452 // Add sql filters
453 if ($sqlfilters) {
454 $errormessage = '';
455 $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
456 if ($errormessage) {
457 throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage);
458 }
459 }
460
461 $sql .= " ORDER BY rowid";
462
463 $result = $this->db->query($sql);
464
465 if ($result) {
466 $num = $this->db->num_rows($result);
467 for ($i = 0; $i < $num; $i++) {
468 $obj = $this->db->fetch_object($result);
469 $accountLine = new AccountLine($this->db);
470 if ($accountLine->fetch($obj->rowid) > 0) {
471 $list[] = $this->_cleanObjectDatas($accountLine);
472 }
473 }
474 } else {
475 throw new RestException(503, 'Error when retrieving list of account lines: '.$this->db->lasterror());
476 }
477
478 return $list;
479 }
480
500 public function addLine($id, $date, $type, $label, $amount, $category = 0, $cheque_number = '', $cheque_writer = '', $cheque_bank = '', $accountancycode = '', $datev = null, $num_releve = '')
501 {
502 if (!DolibarrApiAccess::$user->rights->banque->modifier) {
503 throw new RestException(401);
504 }
505
506 $account = new Account($this->db);
507 $result = $account->fetch($id);
508 if (!$result) {
509 throw new RestException(404, 'account not found');
510 }
511
512 $type = sanitizeVal($type);
513 $label = sanitizeVal($label);
514 $cheque_number = sanitizeVal($cheque_number);
515 $cheque_writer = sanitizeVal($cheque_writer);
516 $cheque_bank = sanitizeVal($cheque_bank);
517 $accountancycode = sanitizeVal($accountancycode);
518 $num_releve = sanitizeVal($num_releve);
519
520 $result = $account->addline(
521 $date,
522 $type,
523 $label,
524 $amount,
525 $cheque_number,
526 $category,
527 DolibarrApiAccess::$user,
528 $cheque_writer,
529 $cheque_bank,
530 $accountancycode,
531 $datev,
532 $num_releve
533 );
534 if ($result < 0) {
535 throw new RestException(503, 'Error when adding line to account: '.$account->error);
536 }
537 return $result;
538 }
539
553 public function addLink($id, $line_id, $url_id, $url, $label, $type)
554 {
555 if (!DolibarrApiAccess::$user->rights->banque->modifier) {
556 throw new RestException(401);
557 }
558
559 $account = new Account($this->db);
560 $result = $account->fetch($id);
561 if (!$result) {
562 throw new RestException(404, 'account not found');
563 }
564
565 $accountLine = new AccountLine($this->db);
566 $result = $accountLine->fetch($line_id);
567 if (!$result) {
568 throw new RestException(404, 'account line not found');
569 }
570
571 $url = sanitizeVal($url);
572 $label = sanitizeVal($label);
573 $type = sanitizeVal($type);
574
575 $result = $account->add_url_line($line_id, $url_id, $url, $label, $type);
576 if ($result < 0) {
577 throw new RestException(503, 'Error when adding link to account line: '.$account->error);
578 }
579 return $result;
580 }
581
594 public function getLinks($id, $line_id)
595 {
596 $list = array();
597
598 if (!DolibarrApiAccess::$user->rights->banque->lire) {
599 throw new RestException(401);
600 }
601
602 $account = new Account($this->db);
603 $result = $account->fetch($id);
604 if (!$result) {
605 throw new RestException(404, 'account not found');
606 }
607 $links = $account->get_url($line_id); // Get an array('url'=>, 'url_id'=>, 'label'=>, 'type'=> 'fk_bank'=> )
608 foreach ($links as &$link) {
609 unset($link[0], $link[1], $link[2], $link[3]); // Remove the numeric keys
610 }
611
612 return $links;
613 }
614}
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.
__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.
transfer($bankaccount_from_id=0, $bankaccount_to_id=0, $date=null, $description="", $amount=0.0, $amount_to=0.0)
Create an internal wire transfer between two bank accounts.
getLines($id, $sqlfilters='')
Get the list of lines of the account.
static $FIELDS
array $FIELDS Mandatory fields, checked when creating an object
_cleanObjectDatas($object)
Clean sensible object datas.
Class for API REST v1.
Definition api.class.php:31
_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:85
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.