dolibarr 19.0.4
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 if ($field == 'array_options' && is_array($value)) {
349 foreach ($value as $index => $val) {
350 $account->array_options[$index] = $this->_checkValForAPI($field, $val, $account);
351 }
352 continue;
353 }
354 $account->$field = $this->_checkValForAPI($field, $value, $account);
355 }
356
357 if ($account->update(DolibarrApiAccess::$user) > 0) {
358 return $this->get($id);
359 } else {
360 throw new RestException(500, $account->error);
361 }
362 }
363
370 public function delete($id)
371 {
372 if (!DolibarrApiAccess::$user->rights->banque->configurer) {
373 throw new RestException(401);
374 }
375 $account = new Account($this->db);
376 $result = $account->fetch($id);
377 if (!$result) {
378 throw new RestException(404, 'account not found');
379 }
380
381 if ($account->delete(DolibarrApiAccess::$user) < 0) {
382 throw new RestException(401, 'error when deleting account');
383 }
384
385 return array(
386 'success' => array(
387 'code' => 200,
388 'message' => 'account deleted'
389 )
390 );
391 }
392
401 private function _validate($data)
402 {
403 $account = array();
404 foreach (BankAccounts::$FIELDS as $field) {
405 if (!isset($data[$field])) {
406 throw new RestException(400, "$field field missing");
407 }
408 $account[$field] = $data[$field];
409 }
410 return $account;
411 }
412
413 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
420 protected function _cleanObjectDatas($object)
421 {
422 // phpcs:enable
423 $object = parent::_cleanObjectDatas($object);
424
425 unset($object->rowid);
426
427 return $object;
428 }
429
441 public function getLines($id, $sqlfilters = '')
442 {
443 $list = array();
444
445 if (!DolibarrApiAccess::$user->rights->banque->lire) {
446 throw new RestException(401);
447 }
448
449 $account = new Account($this->db);
450 $result = $account->fetch($id);
451 if (!$result) {
452 throw new RestException(404, 'account not found');
453 }
454
455 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."bank ";
456 $sql .= " WHERE fk_account = ".((int) $id);
457
458 // Add sql filters
459 if ($sqlfilters) {
460 $errormessage = '';
461 $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
462 if ($errormessage) {
463 throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage);
464 }
465 }
466
467 $sql .= " ORDER BY rowid";
468
469 $result = $this->db->query($sql);
470
471 if ($result) {
472 $num = $this->db->num_rows($result);
473 for ($i = 0; $i < $num; $i++) {
474 $obj = $this->db->fetch_object($result);
475 $accountLine = new AccountLine($this->db);
476 if ($accountLine->fetch($obj->rowid) > 0) {
477 $list[] = $this->_cleanObjectDatas($accountLine);
478 }
479 }
480 } else {
481 throw new RestException(503, 'Error when retrieving list of account lines: '.$this->db->lasterror());
482 }
483
484 return $list;
485 }
486
506 public function addLine($id, $date, $type, $label, $amount, $category = 0, $cheque_number = '', $cheque_writer = '', $cheque_bank = '', $accountancycode = '', $datev = null, $num_releve = '')
507 {
508 if (!DolibarrApiAccess::$user->rights->banque->modifier) {
509 throw new RestException(401);
510 }
511
512 $account = new Account($this->db);
513 $result = $account->fetch($id);
514 if (!$result) {
515 throw new RestException(404, 'account not found');
516 }
517
518 $type = sanitizeVal($type);
519 $label = sanitizeVal($label);
520 $cheque_number = sanitizeVal($cheque_number);
521 $cheque_writer = sanitizeVal($cheque_writer);
522 $cheque_bank = sanitizeVal($cheque_bank);
523 $accountancycode = sanitizeVal($accountancycode);
524 $num_releve = sanitizeVal($num_releve);
525
526 $result = $account->addline(
527 $date,
528 $type,
529 $label,
530 $amount,
531 $cheque_number,
532 $category,
533 DolibarrApiAccess::$user,
534 $cheque_writer,
535 $cheque_bank,
536 $accountancycode,
537 $datev,
538 $num_releve
539 );
540 if ($result < 0) {
541 throw new RestException(503, 'Error when adding line to account: '.$account->error);
542 }
543 return $result;
544 }
545
559 public function addLink($id, $line_id, $url_id, $url, $label, $type)
560 {
561 if (!DolibarrApiAccess::$user->rights->banque->modifier) {
562 throw new RestException(401);
563 }
564
565 $account = new Account($this->db);
566 $result = $account->fetch($id);
567 if (!$result) {
568 throw new RestException(404, 'account not found');
569 }
570
571 $accountLine = new AccountLine($this->db);
572 $result = $accountLine->fetch($line_id);
573 if (!$result) {
574 throw new RestException(404, 'account line not found');
575 }
576
577 $url = sanitizeVal($url);
578 $label = sanitizeVal($label);
579 $type = sanitizeVal($type);
580
581 $result = $account->add_url_line($line_id, $url_id, $url, $label, $type);
582 if ($result < 0) {
583 throw new RestException(503, 'Error when adding link to account line: '.$account->error);
584 }
585 return $result;
586 }
587
600 public function getLinks($id, $line_id)
601 {
602 $list = array();
603
604 if (!DolibarrApiAccess::$user->rights->banque->lire) {
605 throw new RestException(401);
606 }
607
608 $account = new Account($this->db);
609 $result = $account->fetch($id);
610 if (!$result) {
611 throw new RestException(404, 'account not found');
612 }
613 $links = $account->get_url($line_id); // Get an array('url'=>, 'url_id'=>, 'label'=>, 'type'=> 'fk_bank'=> )
614 foreach ($links as &$link) {
615 unset($link[0], $link[1], $link[2], $link[3]); // Remove the numeric keys
616 }
617
618 return $links;
619 }
620}
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.