PageRenderTime 24ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/htdocs/compta/bank/class/api_bankaccounts.class.php

http://github.com/Dolibarr/dolibarr
PHP | 568 lines | 332 code | 71 blank | 165 comment | 65 complexity | 42f8bd0ef6f5353eca986ff26f4fd5dc MD5 | raw file
Possible License(s): GPL-2.0, AGPL-3.0, LGPL-2.0, CC-BY-SA-4.0, BSD-3-Clause, MPL-2.0-no-copyleft-exception, LGPL-3.0, GPL-3.0, LGPL-2.1, MIT
  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. use Luracast\Restler\RestException;
  19. require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
  20. /**
  21. * API class for accounts
  22. *
  23. * @property DoliDB db
  24. * @access protected
  25. * @class DolibarrApiAccess {@requires user,external}
  26. */
  27. class BankAccounts extends DolibarrApi
  28. {
  29. /**
  30. * array $FIELDS Mandatory fields, checked when creating an object
  31. */
  32. static $FIELDS = array(
  33. 'ref',
  34. 'label',
  35. 'type',
  36. 'currency_code',
  37. 'country_id'
  38. );
  39. /**
  40. * Constructor
  41. */
  42. public function __construct()
  43. {
  44. global $db;
  45. $this->db = $db;
  46. }
  47. /**
  48. * Get the list of accounts.
  49. *
  50. * @param string $sortfield Sort field
  51. * @param string $sortorder Sort order
  52. * @param int $limit Limit for list
  53. * @param int $page Page number
  54. * @param int $category Use this param to filter list by category
  55. * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.ref:like:'SO-%') and (t.import_key:<:'20160101')"
  56. * @return array List of account objects
  57. *
  58. * @throws RestException
  59. */
  60. public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $category = 0, $sqlfilters = '')
  61. {
  62. $list = array();
  63. if (!DolibarrApiAccess::$user->rights->banque->lire) {
  64. throw new RestException(401);
  65. }
  66. $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."bank_account as t";
  67. if ($category > 0) {
  68. $sql .= ", ".MAIN_DB_PREFIX."categorie_account as c";
  69. }
  70. $sql .= ' WHERE t.entity IN ('.getEntity('bank_account').')';
  71. // Select accounts of given category
  72. if ($category > 0) {
  73. $sql .= " AND c.fk_categorie = ".((int) $category)." AND c.fk_account = t.rowid";
  74. }
  75. // Add sql filters
  76. if ($sqlfilters) {
  77. if (!DolibarrApi::_checkFilters($sqlfilters)) {
  78. throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters);
  79. }
  80. $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)';
  81. $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")";
  82. }
  83. $sql .= $this->db->order($sortfield, $sortorder);
  84. if ($limit) {
  85. if ($page < 0) {
  86. $page = 0;
  87. }
  88. $offset = $limit * $page;
  89. $sql .= $this->db->plimit($limit + 1, $offset);
  90. }
  91. dol_syslog("API Rest request");
  92. $result = $this->db->query($sql);
  93. if ($result) {
  94. $num = $this->db->num_rows($result);
  95. $min = min($num, ($limit <= 0 ? $num : $limit));
  96. for ($i = 0; $i < $min; $i++) {
  97. $obj = $this->db->fetch_object($result);
  98. $account = new Account($this->db);
  99. if ($account->fetch($obj->rowid) > 0) {
  100. $list[] = $this->_cleanObjectDatas($account);
  101. }
  102. }
  103. } else {
  104. throw new RestException(503, 'Error when retrieving list of accounts: '.$this->db->lasterror());
  105. }
  106. return $list;
  107. }
  108. /**
  109. * Get account by ID.
  110. *
  111. * @param int $id ID of account
  112. * @return array Account object
  113. *
  114. * @throws RestException
  115. */
  116. public function get($id)
  117. {
  118. if (!DolibarrApiAccess::$user->rights->banque->lire) {
  119. throw new RestException(401);
  120. }
  121. $account = new Account($this->db);
  122. $result = $account->fetch($id);
  123. if (!$result) {
  124. throw new RestException(404, 'account not found');
  125. }
  126. return $this->_cleanObjectDatas($account);
  127. }
  128. /**
  129. * Create account object
  130. *
  131. * @param array $request_data Request data
  132. * @return int ID of account
  133. */
  134. public function post($request_data = null)
  135. {
  136. if (!DolibarrApiAccess::$user->rights->banque->configurer) {
  137. throw new RestException(401);
  138. }
  139. // Check mandatory fields
  140. $result = $this->_validate($request_data);
  141. $account = new Account($this->db);
  142. foreach ($request_data as $field => $value) {
  143. $account->$field = $this->_checkValForAPI($field, $value, $account);
  144. }
  145. // Date of the initial balance (required to create an account).
  146. $account->date_solde = time();
  147. // courant and type are the same thing but the one used when
  148. // creating an account is courant
  149. $account->courant = $account->type;
  150. if ($account->create(DolibarrApiAccess::$user) < 0) {
  151. throw new RestException(500, 'Error creating bank account', array_merge(array($account->error), $account->errors));
  152. }
  153. return $account->id;
  154. }
  155. /**
  156. * Create an internal wire transfer between two bank accounts
  157. *
  158. * @param int $bankaccount_from_id BankAccount ID to use as the source of the internal wire transfer {@from body}{@required true}
  159. * @param int $bankaccount_to_id BankAccount ID to use as the destination of the internal wire transfer {@from body}{@required true}
  160. * @param string $date Date of the internal wire transfer (UNIX timestamp) {@from body}{@required true}{@type timestamp}
  161. * @param string $description Description of the internal wire transfer {@from body}{@required true}
  162. * @param float $amount Amount to transfer from the source to the destination BankAccount {@from body}{@required true}
  163. * @param float $amount_to Amount to transfer to the destination BankAccount (only when accounts does not share the same currency) {@from body}{@required false}
  164. *
  165. * @url POST /transfer
  166. *
  167. * @return array
  168. *
  169. * @status 201
  170. *
  171. * @throws RestException 401 Unauthorized: User does not have permission to configure bank accounts
  172. * @throws RestException 404 Not Found: Either the source or the destination bankaccount for the provided id does not exist
  173. * @throws RestException 422 Unprocessable Entity: Refer to detailed exception message for the cause
  174. * @throws RestException 500 Internal Server Error: Error(s) returned by the RDBMS
  175. */
  176. public function transfer($bankaccount_from_id = 0, $bankaccount_to_id = 0, $date = null, $description = "", $amount = 0.0, $amount_to = 0.0)
  177. {
  178. if (!DolibarrApiAccess::$user->rights->banque->configurer) {
  179. throw new RestException(401);
  180. }
  181. require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
  182. $accountfrom = new Account($this->db);
  183. $resultAccountFrom = $accountfrom->fetch($bankaccount_from_id);
  184. if ($resultAccountFrom === 0) {
  185. throw new RestException(404, 'The BankAccount for bankaccount_from_id provided does not exist.');
  186. }
  187. $accountto = new Account($this->db);
  188. $resultAccountTo = $accountto->fetch($bankaccount_to_id);
  189. if ($resultAccountTo === 0) {
  190. throw new RestException(404, 'The BankAccount for bankaccount_to_id provided does not exist.');
  191. }
  192. if ($accountto->currency_code == $accountfrom->currency_code) {
  193. $amount_to = $amount;
  194. } else {
  195. if (!$amount_to || empty($amount_to)) {
  196. throw new RestException(422, 'You must provide amount_to value since bankaccount_from and bankaccount_to does not share the same currency.');
  197. }
  198. }
  199. if ($amount_to < 0) {
  200. throw new RestException(422, 'You must provide a positive value for amount.');
  201. }
  202. if ($accountto->id == $accountfrom->id) {
  203. throw new RestException(422, 'bankaccount_from_id and bankaccount_to_id must be different !');
  204. }
  205. $this->db->begin();
  206. $error = 0;
  207. $bank_line_id_from = 0;
  208. $bank_line_id_to = 0;
  209. $result = 0;
  210. $user = DolibarrApiAccess::$user;
  211. // By default, electronic transfert from bank to bank
  212. $typefrom = 'PRE';
  213. $typeto = 'VIR';
  214. if ($accountto->courant == Account::TYPE_CASH || $accountfrom->courant == Account::TYPE_CASH) {
  215. // This is transfer of change
  216. $typefrom = 'LIQ';
  217. $typeto = 'LIQ';
  218. }
  219. // Clean data
  220. $description = checkVal($description, 'alphanohtml');
  221. /**
  222. * Creating bank line records
  223. */
  224. if (!$error) {
  225. $bank_line_id_from = $accountfrom->addline($date, $typefrom, $description, -1 * price2num($amount), '', '', $user);
  226. }
  227. if (!($bank_line_id_from > 0)) {
  228. $error++;
  229. }
  230. if (!$error) {
  231. $bank_line_id_to = $accountto->addline($date, $typeto, $description, price2num($amount_to), '', '', $user);
  232. }
  233. if (!($bank_line_id_to > 0)) {
  234. $error++;
  235. }
  236. /**
  237. * Creating links between bank line record and its source
  238. */
  239. $url = DOL_URL_ROOT.'/compta/bank/line.php?rowid=';
  240. $label = '(banktransfert)';
  241. $type = 'banktransfert';
  242. if (!$error) {
  243. $result = $accountfrom->add_url_line($bank_line_id_from, $bank_line_id_to, $url, $label, $type);
  244. }
  245. if (!($result > 0)) {
  246. $error++;
  247. }
  248. if (!$error) {
  249. $result = $accountto->add_url_line($bank_line_id_to, $bank_line_id_from, $url, $label, $type);
  250. }
  251. if (!($result > 0)) {
  252. $error++;
  253. }
  254. if (!$error) {
  255. $this->db->commit();
  256. return array(
  257. 'success' => array(
  258. 'code' => 201,
  259. 'message' => 'Internal wire transfer created successfully.',
  260. 'bank_id_from' => $bank_line_id_from,
  261. 'bank_id_to' => $bank_line_id_to,
  262. )
  263. );
  264. } else {
  265. $this->db->rollback();
  266. throw new RestException(500, $accountfrom->error.' '.$accountto->error);
  267. }
  268. }
  269. /**
  270. * Update account
  271. *
  272. * @param int $id ID of account
  273. * @param array $request_data data
  274. * @return int
  275. */
  276. public function put($id, $request_data = null)
  277. {
  278. if (!DolibarrApiAccess::$user->rights->banque->configurer) {
  279. throw new RestException(401);
  280. }
  281. $account = new Account($this->db);
  282. $result = $account->fetch($id);
  283. if (!$result) {
  284. throw new RestException(404, 'account not found');
  285. }
  286. foreach ($request_data as $field => $value) {
  287. if ($field == 'id') {
  288. continue;
  289. }
  290. $account->$field = $this->_checkValForAPI($field, $value, $account);
  291. }
  292. if ($account->update(DolibarrApiAccess::$user) > 0) {
  293. return $this->get($id);
  294. } else {
  295. throw new RestException(500, $account->error);
  296. }
  297. }
  298. /**
  299. * Delete account
  300. *
  301. * @param int $id ID of account
  302. * @return array
  303. */
  304. public function delete($id)
  305. {
  306. if (!DolibarrApiAccess::$user->rights->banque->configurer) {
  307. throw new RestException(401);
  308. }
  309. $account = new Account($this->db);
  310. $result = $account->fetch($id);
  311. if (!$result) {
  312. throw new RestException(404, 'account not found');
  313. }
  314. if ($account->delete(DolibarrApiAccess::$user) < 0) {
  315. throw new RestException(401, 'error when deleting account');
  316. }
  317. return array(
  318. 'success' => array(
  319. 'code' => 200,
  320. 'message' => 'account deleted'
  321. )
  322. );
  323. }
  324. /**
  325. * Validate fields before creating an object
  326. *
  327. * @param array|null $data Data to validate
  328. * @return array
  329. *
  330. * @throws RestException
  331. */
  332. private function _validate($data)
  333. {
  334. $account = array();
  335. foreach (BankAccounts::$FIELDS as $field) {
  336. if (!isset($data[$field])) {
  337. throw new RestException(400, "$field field missing");
  338. }
  339. $account[$field] = $data[$field];
  340. }
  341. return $account;
  342. }
  343. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
  344. /**
  345. * Clean sensible object datas
  346. *
  347. * @param Object $object Object to clean
  348. * @return Object Object with cleaned properties
  349. */
  350. protected function _cleanObjectDatas($object)
  351. {
  352. // phpcs:enable
  353. $object = parent::_cleanObjectDatas($object);
  354. unset($object->rowid);
  355. return $object;
  356. }
  357. /**
  358. * Get the list of lines of the account.
  359. *
  360. * @param int $id ID of account
  361. * @return array Array of AccountLine objects
  362. *
  363. * @throws RestException
  364. *
  365. * @url GET {id}/lines
  366. * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.ref:like:'SO-%') and (t.import_key:<:'20160101')"
  367. */
  368. public function getLines($id, $sqlfilters = '')
  369. {
  370. $list = array();
  371. if (!DolibarrApiAccess::$user->rights->banque->lire) {
  372. throw new RestException(401);
  373. }
  374. $account = new Account($this->db);
  375. $result = $account->fetch($id);
  376. if (!$result) {
  377. throw new RestException(404, 'account not found');
  378. }
  379. $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."bank ";
  380. $sql .= " WHERE fk_account = ".((int) $id);
  381. // Add sql filters
  382. if ($sqlfilters) {
  383. if (!DolibarrApi::_checkFilters($sqlfilters)) {
  384. throw new RestException(503, 'Error when validating parameter sqlfilters '.$sqlfilters);
  385. }
  386. $regexstring = '\(([^:\'\(\)]+:[^:\'\(\)]+:[^\(\)]+)\)';
  387. $sql .= " AND (".preg_replace_callback('/'.$regexstring.'/', 'DolibarrApi::_forge_criteria_callback', $sqlfilters).")";
  388. }
  389. $sql .= " ORDER BY rowid";
  390. $result = $this->db->query($sql);
  391. if ($result) {
  392. $num = $this->db->num_rows($result);
  393. for ($i = 0; $i < $num; $i++) {
  394. $obj = $this->db->fetch_object($result);
  395. $accountLine = new AccountLine($this->db);
  396. if ($accountLine->fetch($obj->rowid) > 0) {
  397. $list[] = $this->_cleanObjectDatas($accountLine);
  398. }
  399. }
  400. } else {
  401. throw new RestException(503, 'Error when retrieving list of account lines: '.$this->db->lasterror());
  402. }
  403. return $list;
  404. }
  405. /**
  406. * Add a line to an account
  407. *
  408. * @param int $id ID of account
  409. * @param int $date Payment date (timestamp) {@from body} {@type timestamp}
  410. * @param string $type Payment mode (TYP,VIR,PRE,LIQ,VAD,CB,CHQ...) {@from body}
  411. * @param string $label Label {@from body}
  412. * @param float $amount Amount (may be 0) {@from body}
  413. * @param int $category Category
  414. * @param string $cheque_number Cheque numero {@from body}
  415. * @param string $cheque_writer Name of cheque writer {@from body}
  416. * @param string $cheque_bank Bank of cheque writer {@from body}
  417. * @param string $accountancycode Accountancy code {@from body}
  418. * @param int $datev Payment date value (timestamp) {@from body} {@type timestamp}
  419. * @param string $num_releve Bank statement numero {@from body}
  420. * @return int ID of line
  421. *
  422. * @url POST {id}/lines
  423. */
  424. public function addLine($id, $date, $type, $label, $amount, $category = 0, $cheque_number = '', $cheque_writer = '', $cheque_bank = '', $accountancycode = '', $datev = null, $num_releve = '')
  425. {
  426. if (!DolibarrApiAccess::$user->rights->banque->modifier) {
  427. throw new RestException(401);
  428. }
  429. $account = new Account($this->db);
  430. $result = $account->fetch($id);
  431. if (!$result) {
  432. throw new RestException(404, 'account not found');
  433. }
  434. $type = checkVal($type);
  435. $label = checkVal($label);
  436. $cheque_number = checkVal($cheque_number);
  437. $cheque_writer = checkVal($cheque_writer);
  438. $cheque_bank = checkVal($cheque_bank);
  439. $accountancycode = checkVal($accountancycode);
  440. $num_releve = checkVal($num_releve);
  441. $result = $account->addline(
  442. $date,
  443. $type,
  444. $label,
  445. $amount,
  446. $cheque_number,
  447. $category,
  448. DolibarrApiAccess::$user,
  449. $cheque_writer,
  450. $cheque_bank,
  451. $accountancycode,
  452. $datev,
  453. $num_releve
  454. );
  455. if ($result < 0) {
  456. throw new RestException(503, 'Error when adding line to account: '.$account->error);
  457. }
  458. return $result;
  459. }
  460. /**
  461. * Add a link to an account line
  462. *
  463. * @param int $id ID of account
  464. * @param int $line_id ID of account line
  465. * @param int $url_id ID to set in the URL {@from body}
  466. * @param string $url URL of the link {@from body}
  467. * @param string $label Label {@from body}
  468. * @param string $type Type of link ('payment', 'company', 'member', ...) {@from body}
  469. * @return int ID of link
  470. *
  471. * @url POST {id}/lines/{line_id}/links
  472. */
  473. public function addLink($id, $line_id, $url_id, $url, $label, $type)
  474. {
  475. if (!DolibarrApiAccess::$user->rights->banque->modifier) {
  476. throw new RestException(401);
  477. }
  478. $account = new Account($this->db);
  479. $result = $account->fetch($id);
  480. if (!$result) {
  481. throw new RestException(404, 'account not found');
  482. }
  483. $accountLine = new AccountLine($this->db);
  484. $result = $accountLine->fetch($line_id);
  485. if (!$result) {
  486. throw new RestException(404, 'account line not found');
  487. }
  488. $url = checkVal($url);
  489. $label = checkVal($label);
  490. $type = checkVal($type);
  491. $result = $account->add_url_line($line_id, $url_id, $url, $label, $type);
  492. if ($result < 0) {
  493. throw new RestException(503, 'Error when adding link to account line: '.$account->error);
  494. }
  495. return $result;
  496. }
  497. }