dolibarr  20.0.0-beta
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 
21 use Luracast\Restler\RestException;
22 
23 require_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  foreach ($request_data as $field => $value) {
163  if ($field === 'caller') {
164  // 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
165  $account->context['caller'] = sanitizeVal($request_data['caller'], 'aZ09');
166  continue;
167  }
168 
169  $account->$field = $this->_checkValForAPI($field, $value, $account);
170  }
171  // Date of the initial balance (required to create an account).
172  $account->date_solde = time();
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 }
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.
Definition: api.class.php:136
_checkValForAPI($field, $value, $object)
Check and convert a string depending on its type/name.
Definition: api.class.php:82
if(isModEnabled('invoice') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') && $user->hasRight('don', 'lire')) if(isModEnabled('tax') && $user->hasRight('tax', 'charges', 'lire')) if(isModEnabled('invoice') &&isModEnabled('order') && $user->hasRight("commande", "lire") &&!getDolGlobalString('WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER')) $sql
Social contributions to pay.
Definition: index.php:745
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.