api_invoices.class.php 57 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802
  1. <?php
  2. /* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
  3. * Copyright (C) 2020 Thibault FOUCART <support@ptibogxiv.net>
  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/facture/class/facture.class.php';
  20. require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture-rec.class.php';
  21. /**
  22. * API class for invoices
  23. *
  24. * @access protected
  25. * @class DolibarrApiAccess {@requires user,external}
  26. */
  27. class Invoices extends DolibarrApi
  28. {
  29. /**
  30. *
  31. * @var array $FIELDS Mandatory fields, checked when create and update object
  32. */
  33. public static $FIELDS = array(
  34. 'socid',
  35. );
  36. /**
  37. * @var Facture $invoice {@type Facture}
  38. */
  39. private $invoice;
  40. /**
  41. * @var FactureRec $templte_invoice {@type FactureRec}
  42. */
  43. private $template_invoice;
  44. /**
  45. * Constructor
  46. */
  47. public function __construct()
  48. {
  49. global $db, $conf;
  50. $this->db = $db;
  51. $this->invoice = new Facture($this->db);
  52. $this->template_invoice = new FactureRec($this->db);
  53. }
  54. /**
  55. * Get properties of a invoice object
  56. *
  57. * Return an array with invoice informations
  58. *
  59. * @param int $id ID of invoice
  60. * @param int $contact_list 0:Return array contains all properties, 1:Return array contains just id, -1: Do not return contacts/adddesses
  61. * @return Object Object with cleaned properties
  62. *
  63. * @throws RestException
  64. */
  65. public function get($id, $contact_list = 1)
  66. {
  67. return $this->_fetch($id, '', '', $contact_list);
  68. }
  69. /**
  70. * Get properties of an invoice object by ref
  71. *
  72. * Return an array with invoice informations
  73. *
  74. * @param string $ref Ref of object
  75. * @param int $contact_list 0: Returned array of contacts/addresses contains all properties, 1: Return array contains just id, -1: Do not return contacts/adddesses
  76. * @return Object Object with cleaned properties
  77. *
  78. * @url GET ref/{ref}
  79. *
  80. * @throws RestException
  81. */
  82. public function getByRef($ref, $contact_list = 1)
  83. {
  84. return $this->_fetch('', $ref, '', $contact_list);
  85. }
  86. /**
  87. * Get properties of an invoice object by ref_ext
  88. *
  89. * Return an array with invoice informations
  90. *
  91. * @param string $ref_ext External reference of object
  92. * @param int $contact_list 0: Returned array of contacts/addresses contains all properties, 1: Return array contains just id, -1: Do not return contacts/adddesses
  93. * @return Object Object with cleaned properties
  94. *
  95. * @url GET ref_ext/{ref_ext}
  96. *
  97. * @throws RestException
  98. */
  99. public function getByRefExt($ref_ext, $contact_list = 1)
  100. {
  101. return $this->_fetch('', '', $ref_ext, $contact_list);
  102. }
  103. /**
  104. * Get properties of an invoice object
  105. *
  106. * Return an array with invoice informations
  107. *
  108. * @param int $id ID of order
  109. * @param string $ref Ref of object
  110. * @param string $ref_ext External reference of object
  111. * @param int $contact_list 0: Returned array of contacts/addresses contains all properties, 1: Return array contains just id, -1: Do not return contacts/adddesses
  112. * @return Object Object with cleaned properties
  113. *
  114. * @throws RestException
  115. */
  116. private function _fetch($id, $ref = '', $ref_ext = '', $contact_list = 1)
  117. {
  118. if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
  119. throw new RestException(401);
  120. }
  121. $result = $this->invoice->fetch($id, $ref, $ref_ext);
  122. if (!$result) {
  123. throw new RestException(404, 'Invoice not found');
  124. }
  125. // Get payment details
  126. $this->invoice->totalpaid = $this->invoice->getSommePaiement();
  127. $this->invoice->totalcreditnotes = $this->invoice->getSumCreditNotesUsed();
  128. $this->invoice->totaldeposits = $this->invoice->getSumDepositsUsed();
  129. $this->invoice->remaintopay = price2num($this->invoice->total_ttc - $this->invoice->totalpaid - $this->invoice->totalcreditnotes - $this->invoice->totaldeposits, 'MT');
  130. if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
  131. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  132. }
  133. // Add external contacts ids
  134. if ($contact_list > -1) {
  135. $tmparray = $this->invoice->liste_contact(-1, 'external', $contact_list);
  136. if (is_array($tmparray)) {
  137. $this->invoice->contacts_ids = $tmparray;
  138. }
  139. }
  140. $this->invoice->fetchObjectLinked();
  141. return $this->_cleanObjectDatas($this->invoice);
  142. }
  143. /**
  144. * List invoices
  145. *
  146. * Get a list of invoices
  147. *
  148. * @param string $sortfield Sort field
  149. * @param string $sortorder Sort order
  150. * @param int $limit Limit for list
  151. * @param int $page Page number
  152. * @param string $thirdparty_ids Thirdparty ids to filter orders of (example '1' or '1,2,3') {@pattern /^[0-9,]*$/i}
  153. * @param string $status Filter by invoice status : draft | unpaid | paid | cancelled
  154. * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.ref:like:'SO-%') and (t.date_creation:<:'20160101')"
  155. * @param string $properties Restrict the data returned to theses properties. Ignored if empty. Comma separated list of properties names
  156. * @return array Array of invoice objects
  157. *
  158. * @throws RestException 404 Not found
  159. * @throws RestException 503 Error
  160. */
  161. public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $thirdparty_ids = '', $status = '', $sqlfilters = '', $properties = '')
  162. {
  163. global $db, $conf;
  164. if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
  165. throw new RestException(401);
  166. }
  167. $obj_ret = array();
  168. // case of external user, $thirdparty_ids param is ignored and replaced by user's socid
  169. $socids = DolibarrApiAccess::$user->socid ? DolibarrApiAccess::$user->socid : $thirdparty_ids;
  170. // If the internal user must only see his customers, force searching by him
  171. $search_sale = 0;
  172. if (!DolibarrApiAccess::$user->rights->societe->client->voir && !$socids) {
  173. $search_sale = DolibarrApiAccess::$user->id;
  174. }
  175. $sql = "SELECT t.rowid";
  176. if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socids) || $search_sale > 0) {
  177. $sql .= ", sc.fk_soc, sc.fk_user"; // We need these fields in order to filter by sale (including the case where the user can only see his prospects)
  178. }
  179. $sql .= " FROM ".MAIN_DB_PREFIX."facture AS t LEFT JOIN ".MAIN_DB_PREFIX."facture_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
  180. if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socids) || $search_sale > 0) {
  181. $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; // We need this table joined to the select in order to filter by sale
  182. }
  183. $sql .= ' WHERE t.entity IN ('.getEntity('invoice').')';
  184. if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socids) || $search_sale > 0) {
  185. $sql .= " AND t.fk_soc = sc.fk_soc";
  186. }
  187. if ($socids) {
  188. $sql .= " AND t.fk_soc IN (".$this->db->sanitize($socids).")";
  189. }
  190. if ($search_sale > 0) {
  191. $sql .= " AND t.rowid = sc.fk_soc"; // Join for the needed table to filter by sale
  192. }
  193. // Filter by status
  194. if ($status == 'draft') {
  195. $sql .= " AND t.fk_statut IN (0)";
  196. }
  197. if ($status == 'unpaid') {
  198. $sql .= " AND t.fk_statut IN (1)";
  199. }
  200. if ($status == 'paid') {
  201. $sql .= " AND t.fk_statut IN (2)";
  202. }
  203. if ($status == 'cancelled') {
  204. $sql .= " AND t.fk_statut IN (3)";
  205. }
  206. // Insert sale filter
  207. if ($search_sale > 0) {
  208. $sql .= " AND sc.fk_user = ".((int) $search_sale);
  209. }
  210. // Add sql filters
  211. if ($sqlfilters) {
  212. $errormessage = '';
  213. $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
  214. if ($errormessage) {
  215. throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage);
  216. }
  217. }
  218. $sql .= $this->db->order($sortfield, $sortorder);
  219. if ($limit) {
  220. if ($page < 0) {
  221. $page = 0;
  222. }
  223. $offset = $limit * $page;
  224. $sql .= $this->db->plimit($limit + 1, $offset);
  225. }
  226. $result = $this->db->query($sql);
  227. if ($result) {
  228. $i = 0;
  229. $num = $this->db->num_rows($result);
  230. $min = min($num, ($limit <= 0 ? $num : $limit));
  231. while ($i < $min) {
  232. $obj = $this->db->fetch_object($result);
  233. $invoice_static = new Facture($this->db);
  234. if ($invoice_static->fetch($obj->rowid)) {
  235. // Get payment details
  236. $invoice_static->totalpaid = $invoice_static->getSommePaiement();
  237. $invoice_static->totalcreditnotes = $invoice_static->getSumCreditNotesUsed();
  238. $invoice_static->totaldeposits = $invoice_static->getSumDepositsUsed();
  239. $invoice_static->remaintopay = price2num($invoice_static->total_ttc - $invoice_static->totalpaid - $invoice_static->totalcreditnotes - $invoice_static->totaldeposits, 'MT');
  240. // Add external contacts ids
  241. $tmparray = $invoice_static->liste_contact(-1, 'external', 1);
  242. if (is_array($tmparray)) {
  243. $invoice_static->contacts_ids = $tmparray;
  244. }
  245. $obj_ret[] = $this->_filterObjectProperties($this->_cleanObjectDatas($invoice_static), $properties);
  246. }
  247. $i++;
  248. }
  249. } else {
  250. throw new RestException(503, 'Error when retrieve invoice list : '.$this->db->lasterror());
  251. }
  252. return $obj_ret;
  253. }
  254. /**
  255. * Create invoice object
  256. *
  257. * @param array $request_data Request datas
  258. * @return int ID of invoice
  259. */
  260. public function post($request_data = null)
  261. {
  262. if (!DolibarrApiAccess::$user->rights->facture->creer) {
  263. throw new RestException(401, "Insuffisant rights");
  264. }
  265. // Check mandatory fields
  266. $result = $this->_validate($request_data);
  267. foreach ($request_data as $field => $value) {
  268. if ($field === 'caller') {
  269. // 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
  270. $this->invoice->context['caller'] = $request_data['caller'];
  271. continue;
  272. }
  273. $this->invoice->$field = $value;
  274. }
  275. if (!array_key_exists('date', $request_data)) {
  276. $this->invoice->date = dol_now();
  277. }
  278. /* We keep lines as an array
  279. if (isset($request_data["lines"])) {
  280. $lines = array();
  281. foreach ($request_data["lines"] as $line) {
  282. array_push($lines, (object) $line);
  283. }
  284. $this->invoice->lines = $lines;
  285. }*/
  286. if ($this->invoice->create(DolibarrApiAccess::$user, 0, (empty($request_data["date_lim_reglement"]) ? 0 : $request_data["date_lim_reglement"])) < 0) {
  287. throw new RestException(500, "Error creating invoice", array_merge(array($this->invoice->error), $this->invoice->errors));
  288. }
  289. return $this->invoice->id;
  290. }
  291. /**
  292. * Create an invoice using an existing order.
  293. *
  294. * @param int $orderid Id of the order
  295. * @return Object Object with cleaned properties
  296. *
  297. * @url POST /createfromorder/{orderid}
  298. *
  299. * @throws RestException 400
  300. * @throws RestException 401
  301. * @throws RestException 404
  302. * @throws RestException 405
  303. */
  304. public function createInvoiceFromOrder($orderid)
  305. {
  306. require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
  307. if (!DolibarrApiAccess::$user->hasRight('commande', 'lire')) {
  308. throw new RestException(401);
  309. }
  310. if (!DolibarrApiAccess::$user->rights->facture->creer) {
  311. throw new RestException(401);
  312. }
  313. if (empty($orderid)) {
  314. throw new RestException(400, 'Order ID is mandatory');
  315. }
  316. $order = new Commande($this->db);
  317. $result = $order->fetch($orderid);
  318. if (!$result) {
  319. throw new RestException(404, 'Order not found');
  320. }
  321. $result = $this->invoice->createFromOrder($order, DolibarrApiAccess::$user);
  322. if ($result < 0) {
  323. throw new RestException(405, $this->invoice->error);
  324. }
  325. $this->invoice->fetchObjectLinked();
  326. return $this->_cleanObjectDatas($this->invoice);
  327. }
  328. /**
  329. * Get lines of an invoice
  330. *
  331. * @param int $id Id of invoice
  332. * @return array Array of lines
  333. *
  334. * @url GET {id}/lines
  335. */
  336. public function getLines($id)
  337. {
  338. if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
  339. throw new RestException(401);
  340. }
  341. $result = $this->invoice->fetch($id);
  342. if (!$result) {
  343. throw new RestException(404, 'Invoice not found');
  344. }
  345. if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
  346. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  347. }
  348. $this->invoice->getLinesArray();
  349. $result = array();
  350. foreach ($this->invoice->lines as $line) {
  351. array_push($result, $this->_cleanObjectDatas($line));
  352. }
  353. return $result;
  354. }
  355. /**
  356. * Update a line to a given invoice
  357. *
  358. * @param int $id Id of invoice to update
  359. * @param int $lineid Id of line to update
  360. * @param array $request_data InvoiceLine data
  361. * @return Object Object with cleaned properties
  362. *
  363. * @url PUT {id}/lines/{lineid}
  364. *
  365. * @throws RestException 304
  366. * @throws RestException 401
  367. * @throws RestException 404 Invoice not found
  368. */
  369. public function putLine($id, $lineid, $request_data = null)
  370. {
  371. if (!DolibarrApiAccess::$user->rights->facture->creer) {
  372. throw new RestException(401);
  373. }
  374. $result = $this->invoice->fetch($id);
  375. if (!$result) {
  376. throw new RestException(404, 'Invoice not found');
  377. }
  378. if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
  379. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  380. }
  381. $request_data = (object) $request_data;
  382. $request_data->desc = sanitizeVal($request_data->desc, 'restricthtml');
  383. $request_data->label = sanitizeVal($request_data->label);
  384. $updateRes = $this->invoice->updateline(
  385. $lineid,
  386. $request_data->desc,
  387. $request_data->subprice,
  388. $request_data->qty,
  389. $request_data->remise_percent,
  390. $request_data->date_start,
  391. $request_data->date_end,
  392. $request_data->tva_tx,
  393. $request_data->localtax1_tx,
  394. $request_data->localtax2_tx,
  395. $request_data->price_base_type ? $request_data->price_base_type : 'HT',
  396. $request_data->info_bits,
  397. $request_data->product_type,
  398. $request_data->fk_parent_line,
  399. 0,
  400. $request_data->fk_fournprice,
  401. $request_data->pa_ht,
  402. $request_data->label,
  403. $request_data->special_code,
  404. $request_data->array_options,
  405. $request_data->situation_percent,
  406. $request_data->fk_unit,
  407. $request_data->multicurrency_subprice,
  408. 0,
  409. $request_data->ref_ext,
  410. $request_data->rang
  411. );
  412. if ($updateRes > 0) {
  413. $result = $this->get($id);
  414. unset($result->line);
  415. return $this->_cleanObjectDatas($result);
  416. } else {
  417. throw new RestException(304, $this->invoice->error);
  418. }
  419. }
  420. /**
  421. * Add a contact type of given invoice
  422. *
  423. * @param int $id Id of invoice to update
  424. * @param int $contactid Id of contact to add
  425. * @param string $type Type of the contact (BILLING, SHIPPING, CUSTOMER)
  426. * @return array
  427. *
  428. * @url POST {id}/contact/{contactid}/{type}
  429. *
  430. * @throws RestException 401
  431. * @throws RestException 404
  432. */
  433. public function postContact($id, $contactid, $type)
  434. {
  435. if (!DolibarrApiAccess::$user->rights->facture->creer) {
  436. throw new RestException(401);
  437. }
  438. $result = $this->invoice->fetch($id);
  439. if (!$result) {
  440. throw new RestException(404, 'Invoice not found');
  441. }
  442. if (!in_array($type, array('BILLING', 'SHIPPING', 'CUSTOMER'), true)) {
  443. throw new RestException(500, 'Availables types: BILLING, SHIPPING OR CUSTOMER');
  444. }
  445. if (!DolibarrApi::_checkAccessToResource('invoice', $this->invoice->id)) {
  446. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  447. }
  448. $result = $this->invoice->add_contact($contactid, $type, 'external');
  449. if (!$result) {
  450. throw new RestException(500, 'Error when added the contact');
  451. }
  452. return array(
  453. 'success' => array(
  454. 'code' => 200,
  455. 'message' => 'Contact linked to the invoice'
  456. )
  457. );
  458. }
  459. /**
  460. * Delete a contact type of given invoice
  461. *
  462. * @param int $id Id of invoice to update
  463. * @param int $contactid Row key of the contact in the array contact_ids.
  464. * @param string $type Type of the contact (BILLING, SHIPPING, CUSTOMER).
  465. * @return Object Object with cleaned properties
  466. *
  467. * @url DELETE {id}/contact/{contactid}/{type}
  468. *
  469. * @throws RestException 401
  470. * @throws RestException 404
  471. * @throws RestException 500 System error
  472. */
  473. public function deleteContact($id, $contactid, $type)
  474. {
  475. if (!DolibarrApiAccess::$user->rights->facture->creer) {
  476. throw new RestException(401);
  477. }
  478. $result = $this->invoice->fetch($id);
  479. if (!$result) {
  480. throw new RestException(404, 'Invoice not found');
  481. }
  482. if (!DolibarrApi::_checkAccessToResource('invoice', $this->invoice->id)) {
  483. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  484. }
  485. $contacts = $this->invoice->liste_contact();
  486. foreach ($contacts as $contact) {
  487. if ($contact['id'] == $contactid && $contact['code'] == $type) {
  488. $result = $this->invoice->delete_contact($contact['rowid']);
  489. if (!$result) {
  490. throw new RestException(500, 'Error when deleted the contact');
  491. }
  492. }
  493. }
  494. return $this->_cleanObjectDatas($this->invoice);
  495. }
  496. /**
  497. * Deletes a line of a given invoice
  498. *
  499. * @param int $id Id of invoice
  500. * @param int $lineid Id of the line to delete
  501. * @return Object Object with cleaned properties
  502. *
  503. * @url DELETE {id}/lines/{lineid}
  504. *
  505. * @throws RestException 400
  506. * @throws RestException 401
  507. * @throws RestException 404
  508. * @throws RestException 405
  509. */
  510. public function deleteLine($id, $lineid)
  511. {
  512. if (!DolibarrApiAccess::$user->rights->facture->creer) {
  513. throw new RestException(401);
  514. }
  515. if (empty($lineid)) {
  516. throw new RestException(400, 'Line ID is mandatory');
  517. }
  518. if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
  519. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  520. }
  521. $result = $this->invoice->fetch($id);
  522. if (!$result) {
  523. throw new RestException(404, 'Invoice not found');
  524. }
  525. $updateRes = $this->invoice->deleteline($lineid, $id);
  526. if ($updateRes > 0) {
  527. return $this->get($id);
  528. } else {
  529. throw new RestException(405, $this->invoice->error);
  530. }
  531. }
  532. /**
  533. * Update invoice
  534. *
  535. * @param int $id Id of invoice to update
  536. * @param array $request_data Datas
  537. * @return Object|false Object with cleaned properties
  538. */
  539. public function put($id, $request_data = null)
  540. {
  541. if (!DolibarrApiAccess::$user->rights->facture->creer) {
  542. throw new RestException(401);
  543. }
  544. $result = $this->invoice->fetch($id);
  545. if (!$result) {
  546. throw new RestException(404, 'Invoice not found');
  547. }
  548. if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
  549. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  550. }
  551. foreach ($request_data as $field => $value) {
  552. if ($field == 'id') {
  553. continue;
  554. }
  555. if ($field === 'caller') {
  556. // 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
  557. $this->invoice->context['caller'] = $request_data['caller'];
  558. continue;
  559. }
  560. $this->invoice->$field = $value;
  561. }
  562. // update bank account
  563. if (!empty($this->invoice->fk_account)) {
  564. if ($this->invoice->setBankAccount($this->invoice->fk_account) == 0) {
  565. throw new RestException(400, $this->invoice->error);
  566. }
  567. }
  568. if ($this->invoice->update(DolibarrApiAccess::$user)) {
  569. return $this->get($id);
  570. }
  571. return false;
  572. }
  573. /**
  574. * Delete invoice
  575. *
  576. * @param int $id Invoice ID
  577. * @return array
  578. */
  579. public function delete($id)
  580. {
  581. if (!DolibarrApiAccess::$user->hasRight('facture', 'supprimer')) {
  582. throw new RestException(401);
  583. }
  584. $result = $this->invoice->fetch($id);
  585. if (!$result) {
  586. throw new RestException(404, 'Invoice not found');
  587. }
  588. if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
  589. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  590. }
  591. $result = $this->invoice->delete(DolibarrApiAccess::$user);
  592. if ($result < 0) {
  593. throw new RestException(500, 'Error when deleting invoice');
  594. } elseif ($result == 0) {
  595. throw new RestException(403, 'Invoice not erasable');
  596. }
  597. return array(
  598. 'success' => array(
  599. 'code' => 200,
  600. 'message' => 'Invoice deleted'
  601. )
  602. );
  603. }
  604. /**
  605. * Add a line to a given invoice
  606. *
  607. * Exemple of POST query :
  608. * {
  609. * "desc": "Desc", "subprice": "1.00000000", "qty": "1", "tva_tx": "20.000", "localtax1_tx": "0.000", "localtax2_tx": "0.000",
  610. * "fk_product": "1", "remise_percent": "0", "date_start": "", "date_end": "", "fk_code_ventilation": 0, "info_bits": "0",
  611. * "fk_remise_except": null, "product_type": "1", "rang": "-1", "special_code": "0", "fk_parent_line": null, "fk_fournprice": null,
  612. * "pa_ht": "0.00000000", "label": "", "array_options": [], "situation_percent": "100", "fk_prev_id": null, "fk_unit": null
  613. * }
  614. *
  615. * @param int $id Id of invoice
  616. * @param array $request_data InvoiceLine data
  617. *
  618. * @url POST {id}/lines
  619. *
  620. * @return int
  621. *
  622. * @throws RestException 304
  623. * @throws RestException 401
  624. * @throws RestException 404
  625. * @throws RestException 400
  626. */
  627. public function postLine($id, $request_data = null)
  628. {
  629. if (!DolibarrApiAccess::$user->rights->facture->creer) {
  630. throw new RestException(401);
  631. }
  632. $result = $this->invoice->fetch($id);
  633. if (!$result) {
  634. throw new RestException(404, 'Invoice not found');
  635. }
  636. if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
  637. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  638. }
  639. $request_data = (object) $request_data;
  640. $request_data->desc = sanitizeVal($request_data->desc, 'restricthtml');
  641. $request_data->label = sanitizeVal($request_data->label);
  642. // Reset fk_parent_line for no child products and special product
  643. if (($request_data->product_type != 9 && empty($request_data->fk_parent_line)) || $request_data->product_type == 9) {
  644. $request_data->fk_parent_line = 0;
  645. }
  646. // calculate pa_ht
  647. $marginInfos = getMarginInfos($request_data->subprice, $request_data->remise_percent, $request_data->tva_tx, $request_data->localtax1_tx, $request_data->localtax2_tx, $request_data->fk_fournprice, $request_data->pa_ht);
  648. $pa_ht = $marginInfos[0];
  649. $updateRes = $this->invoice->addline(
  650. $request_data->desc,
  651. $request_data->subprice,
  652. $request_data->qty,
  653. $request_data->tva_tx,
  654. $request_data->localtax1_tx,
  655. $request_data->localtax2_tx,
  656. $request_data->fk_product,
  657. $request_data->remise_percent,
  658. $request_data->date_start,
  659. $request_data->date_end,
  660. $request_data->fk_code_ventilation,
  661. $request_data->info_bits,
  662. $request_data->fk_remise_except,
  663. $request_data->price_base_type ? $request_data->price_base_type : 'HT',
  664. $request_data->subprice,
  665. $request_data->product_type,
  666. $request_data->rang,
  667. $request_data->special_code,
  668. $request_data->origin,
  669. $request_data->origin_id,
  670. $request_data->fk_parent_line,
  671. empty($request_data->fk_fournprice) ? null : $request_data->fk_fournprice,
  672. $pa_ht,
  673. $request_data->label,
  674. $request_data->array_options,
  675. $request_data->situation_percent,
  676. $request_data->fk_prev_id,
  677. $request_data->fk_unit,
  678. 0,
  679. $request_data->ref_ext
  680. );
  681. if ($updateRes < 0) {
  682. throw new RestException(400, 'Unable to insert the new line. Check your inputs. '.$this->invoice->error);
  683. }
  684. return $updateRes;
  685. }
  686. /**
  687. * Adds a contact to an invoice
  688. *
  689. * @param int $id Order ID
  690. * @param int $fk_socpeople Id of thirdparty contact (if source = 'external') or id of user (if souce = 'internal') to link
  691. * @param string $type_contact Type of contact (code). Must a code found into table llx_c_type_contact. For example: BILLING
  692. * @param string $source external=Contact extern (llx_socpeople), internal=Contact intern (llx_user)
  693. * @param int $notrigger Disable all triggers
  694. *
  695. * @url POST {id}/contacts
  696. *
  697. * @return object
  698. *
  699. * @throws RestException 304
  700. * @throws RestException 401
  701. * @throws RestException 404
  702. * @throws RestException 500 System error
  703. *
  704. */
  705. public function addContact($id, $fk_socpeople, $type_contact, $source, $notrigger = 0)
  706. {
  707. if (!DolibarrApiAccess::$user->rights->facture->creer) {
  708. throw new RestException(401);
  709. }
  710. $result = $this->invoice->fetch($id);
  711. if (!$result) {
  712. throw new RestException(404, 'Invoice not found');
  713. }
  714. if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
  715. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  716. }
  717. $result = $this->invoice->add_contact($fk_socpeople, $type_contact, $source, $notrigger);
  718. if ($result < 0) {
  719. throw new RestException(500, 'Error : '.$this->invoice->error);
  720. }
  721. $result = $this->invoice->fetch($id);
  722. if (!$result) {
  723. throw new RestException(404, 'Invoice not found');
  724. }
  725. if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
  726. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  727. }
  728. return $this->_cleanObjectDatas($this->invoice);
  729. }
  730. /**
  731. * Sets an invoice as draft
  732. *
  733. * @param int $id Order ID
  734. * @param int $idwarehouse Warehouse ID
  735. * @return Object Object with cleaned properties
  736. *
  737. * @url POST {id}/settodraft
  738. *
  739. * @throws RestException 304
  740. * @throws RestException 401
  741. * @throws RestException 404
  742. * @throws RestException 500 System error
  743. *
  744. */
  745. public function settodraft($id, $idwarehouse = -1)
  746. {
  747. if (!DolibarrApiAccess::$user->rights->facture->creer) {
  748. throw new RestException(401);
  749. }
  750. $result = $this->invoice->fetch($id);
  751. if (!$result) {
  752. throw new RestException(404, 'Invoice not found');
  753. }
  754. if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
  755. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  756. }
  757. $result = $this->invoice->setDraft(DolibarrApiAccess::$user, $idwarehouse);
  758. if ($result == 0) {
  759. throw new RestException(304, 'Nothing done.');
  760. }
  761. if ($result < 0) {
  762. throw new RestException(500, 'Error : '.$this->invoice->error);
  763. }
  764. $result = $this->invoice->fetch($id);
  765. if (!$result) {
  766. throw new RestException(404, 'Invoice not found');
  767. }
  768. if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
  769. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  770. }
  771. return $this->_cleanObjectDatas($this->invoice);
  772. }
  773. /**
  774. * Validate an invoice
  775. *
  776. * If you get a bad value for param notrigger check that ou provide this in body
  777. * {
  778. * "idwarehouse": 0,
  779. * "notrigger": 0
  780. * }
  781. *
  782. * @param int $id Invoice ID
  783. * @param int $idwarehouse Warehouse ID
  784. * @param int $notrigger 1=Does not execute triggers, 0= execute triggers
  785. * @return Object|false Object with cleaned properties
  786. *
  787. * @url POST {id}/validate
  788. */
  789. public function validate($id, $idwarehouse = 0, $notrigger = 0)
  790. {
  791. if (!DolibarrApiAccess::$user->rights->facture->creer) {
  792. throw new RestException(401);
  793. }
  794. $result = $this->invoice->fetch($id);
  795. if (!$result) {
  796. throw new RestException(404, 'Invoice not found');
  797. }
  798. if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
  799. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  800. }
  801. $result = $this->invoice->validate(DolibarrApiAccess::$user, '', $idwarehouse, $notrigger);
  802. if ($result == 0) {
  803. throw new RestException(304, 'Error nothing done. May be object is already validated');
  804. }
  805. if ($result < 0) {
  806. throw new RestException(500, 'Error when validating Invoice: '.$this->invoice->error);
  807. }
  808. $result = $this->invoice->fetch($id);
  809. if (!$result) {
  810. throw new RestException(404, 'Invoice not found');
  811. }
  812. if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
  813. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  814. }
  815. return $this->_cleanObjectDatas($this->invoice);
  816. }
  817. /**
  818. * Sets an invoice as paid
  819. *
  820. * @param int $id Order ID
  821. * @param string $close_code Code filled if we classify to 'Paid completely' when payment is not complete (for escompte for example)
  822. * @param string $close_note Comment defined if we classify to 'Paid' when payment is not complete (for escompte for example)
  823. * @return Object Object with cleaned properties
  824. *
  825. * @url POST {id}/settopaid
  826. *
  827. * @throws RestException 304
  828. * @throws RestException 401
  829. * @throws RestException 404
  830. * @throws RestException 500 System error
  831. */
  832. public function settopaid($id, $close_code = '', $close_note = '')
  833. {
  834. if (!DolibarrApiAccess::$user->rights->facture->creer) {
  835. throw new RestException(401);
  836. }
  837. $result = $this->invoice->fetch($id);
  838. if (!$result) {
  839. throw new RestException(404, 'Invoice not found');
  840. }
  841. if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
  842. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  843. }
  844. $result = $this->invoice->setPaid(DolibarrApiAccess::$user, $close_code, $close_note);
  845. if ($result == 0) {
  846. throw new RestException(304, 'Error nothing done. May be object is already validated');
  847. }
  848. if ($result < 0) {
  849. throw new RestException(500, 'Error : '.$this->invoice->error);
  850. }
  851. $result = $this->invoice->fetch($id);
  852. if (!$result) {
  853. throw new RestException(404, 'Invoice not found');
  854. }
  855. if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
  856. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  857. }
  858. return $this->_cleanObjectDatas($this->invoice);
  859. }
  860. /**
  861. * Sets an invoice as unpaid
  862. *
  863. * @param int $id Order ID
  864. * @return Object Object with cleaned properties
  865. *
  866. * @url POST {id}/settounpaid
  867. *
  868. * @throws RestException 304
  869. * @throws RestException 401
  870. * @throws RestException 404
  871. * @throws RestException 500 System error
  872. */
  873. public function settounpaid($id)
  874. {
  875. if (!DolibarrApiAccess::$user->rights->facture->creer) {
  876. throw new RestException(401);
  877. }
  878. $result = $this->invoice->fetch($id);
  879. if (!$result) {
  880. throw new RestException(404, 'Invoice not found');
  881. }
  882. if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
  883. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  884. }
  885. $result = $this->invoice->setUnpaid(DolibarrApiAccess::$user);
  886. if ($result == 0) {
  887. throw new RestException(304, 'Nothing done');
  888. }
  889. if ($result < 0) {
  890. throw new RestException(500, 'Error : '.$this->invoice->error);
  891. }
  892. $result = $this->invoice->fetch($id);
  893. if (!$result) {
  894. throw new RestException(404, 'Invoice not found');
  895. }
  896. if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
  897. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  898. }
  899. return $this->_cleanObjectDatas($this->invoice);
  900. }
  901. /**
  902. * Get discount from invoice
  903. *
  904. * @param int $id Id of invoice
  905. * @return Object Object with cleaned properties
  906. *
  907. * @url GET {id}/discount
  908. */
  909. public function getDiscount($id)
  910. {
  911. require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
  912. if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
  913. throw new RestException(401);
  914. }
  915. $result = $this->invoice->fetch($id);
  916. if (!$result) {
  917. throw new RestException(404, 'Invoice not found');
  918. }
  919. if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
  920. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  921. }
  922. $discountcheck = new DiscountAbsolute($this->db);
  923. $result = $discountcheck->fetch(0, $this->invoice->id);
  924. if ($result == 0) {
  925. throw new RestException(404, 'Discount not found');
  926. }
  927. if ($result < 0) {
  928. throw new RestException(500, $discountcheck->error);
  929. }
  930. return parent::_cleanObjectDatas($discountcheck);
  931. }
  932. /**
  933. * Create a discount (credit available) for a credit note or a deposit.
  934. *
  935. * @param int $id Invoice ID
  936. * @return Object Object with cleaned properties
  937. *
  938. * @url POST {id}/markAsCreditAvailable
  939. *
  940. * @throws RestException 304
  941. * @throws RestException 401
  942. * @throws RestException 404
  943. * @throws RestException 500 System error
  944. */
  945. public function markAsCreditAvailable($id)
  946. {
  947. require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
  948. if (!DolibarrApiAccess::$user->rights->facture->creer) {
  949. throw new RestException(401);
  950. }
  951. $result = $this->invoice->fetch($id);
  952. if (!$result) {
  953. throw new RestException(404, 'Invoice not found');
  954. }
  955. if (!DolibarrApi::_checkAccessToResource('facture', $this->invoice->id)) {
  956. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  957. }
  958. if ($this->invoice->paye) {
  959. throw new RestException(500, 'Alreay paid');
  960. }
  961. $this->invoice->fetch($id);
  962. $this->invoice->fetch_thirdparty();
  963. // Check if there is already a discount (protection to avoid duplicate creation when resubmit post)
  964. $discountcheck = new DiscountAbsolute($this->db);
  965. $result = $discountcheck->fetch(0, $this->invoice->id);
  966. $canconvert = 0;
  967. if ($this->invoice->type == Facture::TYPE_DEPOSIT && empty($discountcheck->id)) {
  968. $canconvert = 1; // we can convert deposit into discount if deposit is payed (completely, partially or not at all) and not already converted (see real condition into condition used to show button converttoreduc)
  969. }
  970. if (($this->invoice->type == Facture::TYPE_CREDIT_NOTE || $this->invoice->type == Facture::TYPE_STANDARD) && $this->invoice->paye == 0 && empty($discountcheck->id)) {
  971. $canconvert = 1; // we can convert credit note into discount if credit note is not payed back and not already converted and amount of payment is 0 (see real condition into condition used to show button converttoreduc)
  972. }
  973. if ($canconvert) {
  974. $this->db->begin();
  975. $amount_ht = $amount_tva = $amount_ttc = array();
  976. $multicurrency_amount_ht = $multicurrency_amount_tva = $multicurrency_amount_ttc = array();
  977. // Loop on each vat rate
  978. $i = 0;
  979. foreach ($this->invoice->lines as $line) {
  980. if ($line->product_type < 9 && $line->total_ht != 0) { // Remove lines with product_type greater than or equal to 9
  981. // no need to create discount if amount is null
  982. $amount_ht[$line->tva_tx] += $line->total_ht;
  983. $amount_tva[$line->tva_tx] += $line->total_tva;
  984. $amount_ttc[$line->tva_tx] += $line->total_ttc;
  985. $multicurrency_amount_ht[$line->tva_tx] += $line->multicurrency_total_ht;
  986. $multicurrency_amount_tva[$line->tva_tx] += $line->multicurrency_total_tva;
  987. $multicurrency_amount_ttc[$line->tva_tx] += $line->multicurrency_total_ttc;
  988. $i++;
  989. }
  990. }
  991. // Insert one discount by VAT rate category
  992. $discount = new DiscountAbsolute($this->db);
  993. if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
  994. $discount->description = '(CREDIT_NOTE)';
  995. } elseif ($this->invoice->type == Facture::TYPE_DEPOSIT) {
  996. $discount->description = '(DEPOSIT)';
  997. } elseif ($this->invoice->type == Facture::TYPE_STANDARD || $this->invoice->type == Facture::TYPE_REPLACEMENT || $this->invoice->type == Facture::TYPE_SITUATION) {
  998. $discount->description = '(EXCESS RECEIVED)';
  999. } else {
  1000. throw new RestException(500, 'Cant convert to reduc an Invoice of this type');
  1001. }
  1002. $discount->fk_soc = $this->invoice->socid;
  1003. $discount->fk_facture_source = $this->invoice->id;
  1004. $error = 0;
  1005. if ($this->invoice->type == Facture::TYPE_STANDARD || $this->invoice->type == Facture::TYPE_REPLACEMENT || $this->invoice->type == Facture::TYPE_SITUATION) {
  1006. // If we're on a standard invoice, we have to get excess received to create a discount in TTC without VAT
  1007. // Total payments
  1008. $sql = 'SELECT SUM(pf.amount) as total_payments';
  1009. $sql .= ' FROM '.MAIN_DB_PREFIX.'paiement_facture as pf, '.MAIN_DB_PREFIX.'paiement as p';
  1010. $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as c ON p.fk_paiement = c.id';
  1011. $sql .= ' WHERE pf.fk_facture = '.((int) $this->invoice->id);
  1012. $sql .= ' AND pf.fk_paiement = p.rowid';
  1013. $sql .= ' AND p.entity IN ('.getEntity('invoice').')';
  1014. $resql = $this->db->query($sql);
  1015. if (!$resql) {
  1016. dol_print_error($this->db);
  1017. }
  1018. $res = $this->db->fetch_object($resql);
  1019. $total_payments = $res->total_payments;
  1020. // Total credit note and deposit
  1021. $total_creditnote_and_deposit = 0;
  1022. $sql = "SELECT re.rowid, re.amount_ht, re.amount_tva, re.amount_ttc,";
  1023. $sql .= " re.description, re.fk_facture_source";
  1024. $sql .= " FROM ".MAIN_DB_PREFIX."societe_remise_except as re";
  1025. $sql .= " WHERE fk_facture = ".((int) $this->invoice->id);
  1026. $resql = $this->db->query($sql);
  1027. if (!empty($resql)) {
  1028. while ($obj = $this->db->fetch_object($resql)) {
  1029. $total_creditnote_and_deposit += $obj->amount_ttc;
  1030. }
  1031. } else {
  1032. dol_print_error($this->db);
  1033. }
  1034. $discount->amount_ht = $discount->amount_ttc = $total_payments + $total_creditnote_and_deposit - $this->invoice->total_ttc;
  1035. $discount->amount_tva = 0;
  1036. $discount->tva_tx = 0;
  1037. $result = $discount->create(DolibarrApiAccess::$user);
  1038. if ($result < 0) {
  1039. $error++;
  1040. }
  1041. }
  1042. if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE || $this->invoice->type == Facture::TYPE_DEPOSIT) {
  1043. foreach ($amount_ht as $tva_tx => $xxx) {
  1044. $discount->amount_ht = abs($amount_ht[$tva_tx]);
  1045. $discount->amount_tva = abs($amount_tva[$tva_tx]);
  1046. $discount->amount_ttc = abs($amount_ttc[$tva_tx]);
  1047. $discount->multicurrency_amount_ht = abs($multicurrency_amount_ht[$tva_tx]);
  1048. $discount->multicurrency_amount_tva = abs($multicurrency_amount_tva[$tva_tx]);
  1049. $discount->multicurrency_amount_ttc = abs($multicurrency_amount_ttc[$tva_tx]);
  1050. $discount->tva_tx = abs($tva_tx);
  1051. $result = $discount->create(DolibarrApiAccess::$user);
  1052. if ($result < 0) {
  1053. $error++;
  1054. break;
  1055. }
  1056. }
  1057. }
  1058. if (empty($error)) {
  1059. if ($this->invoice->type != Facture::TYPE_DEPOSIT) {
  1060. // Classe facture
  1061. $result = $this->invoice->setPaid(DolibarrApiAccess::$user);
  1062. if ($result >= 0) {
  1063. $this->db->commit();
  1064. } else {
  1065. $this->db->rollback();
  1066. throw new RestException(500, 'Could not set paid');
  1067. }
  1068. } else {
  1069. $this->db->commit();
  1070. }
  1071. } else {
  1072. $this->db->rollback();
  1073. throw new RestException(500, 'Discount creation error');
  1074. }
  1075. }
  1076. return $this->_cleanObjectDatas($this->invoice);
  1077. }
  1078. /**
  1079. * Add a discount line into an invoice (as an invoice line) using an existing absolute discount
  1080. *
  1081. * Note that this consume the discount.
  1082. *
  1083. * @param int $id Id of invoice
  1084. * @param int $discountid Id of discount
  1085. * @return int
  1086. *
  1087. * @url POST {id}/usediscount/{discountid}
  1088. *
  1089. * @throws RestException 400
  1090. * @throws RestException 401
  1091. * @throws RestException 404
  1092. * @throws RestException 405
  1093. */
  1094. public function useDiscount($id, $discountid)
  1095. {
  1096. if (!DolibarrApiAccess::$user->rights->facture->creer) {
  1097. throw new RestException(401);
  1098. }
  1099. if (empty($id)) {
  1100. throw new RestException(400, 'Invoice ID is mandatory');
  1101. }
  1102. if (empty($discountid)) {
  1103. throw new RestException(400, 'Discount ID is mandatory');
  1104. }
  1105. if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
  1106. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  1107. }
  1108. $result = $this->invoice->fetch($id);
  1109. if (!$result) {
  1110. throw new RestException(404, 'Invoice not found');
  1111. }
  1112. $result = $this->invoice->insert_discount($discountid);
  1113. if ($result < 0) {
  1114. throw new RestException(405, $this->invoice->error);
  1115. }
  1116. return $result;
  1117. }
  1118. /**
  1119. * Add an available credit note discount to payments of an existing invoice.
  1120. *
  1121. * Note that this consume the credit note.
  1122. *
  1123. * @param int $id Id of invoice
  1124. * @param int $discountid Id of a discount coming from a credit note
  1125. * @return int
  1126. *
  1127. * @url POST {id}/usecreditnote/{discountid}
  1128. *
  1129. * @throws RestException 400
  1130. * @throws RestException 401
  1131. * @throws RestException 404
  1132. * @throws RestException 405
  1133. */
  1134. public function useCreditNote($id, $discountid)
  1135. {
  1136. require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
  1137. if (!DolibarrApiAccess::$user->rights->facture->creer) {
  1138. throw new RestException(401);
  1139. }
  1140. if (empty($id)) {
  1141. throw new RestException(400, 'Invoice ID is mandatory');
  1142. }
  1143. if (empty($discountid)) {
  1144. throw new RestException(400, 'Credit ID is mandatory');
  1145. }
  1146. if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
  1147. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  1148. }
  1149. $discount = new DiscountAbsolute($this->db);
  1150. $result = $discount->fetch($discountid);
  1151. if (!$result) {
  1152. throw new RestException(404, 'Credit not found');
  1153. }
  1154. $result = $discount->link_to_invoice(0, $id);
  1155. if ($result < 0) {
  1156. throw new RestException(405, $discount->error);
  1157. }
  1158. return $result;
  1159. }
  1160. /**
  1161. * Get list of payments of a given invoice
  1162. *
  1163. * @param int $id Id of invoice
  1164. * @return array
  1165. *
  1166. * @url GET {id}/payments
  1167. *
  1168. * @throws RestException 400
  1169. * @throws RestException 401
  1170. * @throws RestException 404
  1171. * @throws RestException 405
  1172. */
  1173. public function getPayments($id)
  1174. {
  1175. if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
  1176. throw new RestException(401);
  1177. }
  1178. if (empty($id)) {
  1179. throw new RestException(400, 'Invoice ID is mandatory');
  1180. }
  1181. if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
  1182. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  1183. }
  1184. $result = $this->invoice->fetch($id);
  1185. if (!$result) {
  1186. throw new RestException(404, 'Invoice not found');
  1187. }
  1188. $result = $this->invoice->getListOfPayments();
  1189. if ($result < 0) {
  1190. throw new RestException(405, $this->invoice->error);
  1191. }
  1192. return $result;
  1193. }
  1194. /**
  1195. * Add payment line to a specific invoice with the remain to pay as amount.
  1196. *
  1197. * @param int $id Id of invoice
  1198. * @param string $datepaye {@from body} Payment date {@type timestamp}
  1199. * @param int $paymentid {@from body} Payment mode Id {@min 1}
  1200. * @param string $closepaidinvoices {@from body} Close paid invoices {@choice yes,no}
  1201. * @param int $accountid {@from body} Account Id {@min 1}
  1202. * @param string $num_payment {@from body} Payment number (optional)
  1203. * @param string $comment {@from body} Note private (optional)
  1204. * @param string $chqemetteur {@from body} Payment issuer (mandatory if paymentcode = 'CHQ')
  1205. * @param string $chqbank {@from body} Issuer bank name (optional)
  1206. *
  1207. * @url POST {id}/payments
  1208. *
  1209. * @return int Payment ID
  1210. *
  1211. * @throws RestException 400
  1212. * @throws RestException 401
  1213. * @throws RestException 404
  1214. */
  1215. public function addPayment($id, $datepaye, $paymentid, $closepaidinvoices, $accountid, $num_payment = '', $comment = '', $chqemetteur = '', $chqbank = '')
  1216. {
  1217. require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
  1218. if (!DolibarrApiAccess::$user->rights->facture->creer) {
  1219. throw new RestException(403);
  1220. }
  1221. if (empty($id)) {
  1222. throw new RestException(400, 'Invoice ID is mandatory');
  1223. }
  1224. if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
  1225. throw new RestException(403, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  1226. }
  1227. if (isModEnabled("banque")) {
  1228. if (empty($accountid)) {
  1229. throw new RestException(400, 'Account ID is mandatory');
  1230. }
  1231. }
  1232. if (empty($paymentid)) {
  1233. throw new RestException(400, 'Payment ID or Payment Code is mandatory');
  1234. }
  1235. $result = $this->invoice->fetch($id);
  1236. if (!$result) {
  1237. throw new RestException(404, 'Invoice not found');
  1238. }
  1239. // Calculate amount to pay
  1240. $totalpaid = $this->invoice->getSommePaiement();
  1241. $totalcreditnotes = $this->invoice->getSumCreditNotesUsed();
  1242. $totaldeposits = $this->invoice->getSumDepositsUsed();
  1243. $resteapayer = price2num($this->invoice->total_ttc - $totalpaid - $totalcreditnotes - $totaldeposits, 'MT');
  1244. $this->db->begin();
  1245. $amounts = array();
  1246. $multicurrency_amounts = array();
  1247. // Clean parameters amount if payment is for a credit note
  1248. if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
  1249. $resteapayer = price2num($resteapayer, 'MT');
  1250. $amounts[$id] = price2num(-1 * $resteapayer, 'MT');
  1251. // Multicurrency
  1252. $newvalue = price2num($this->invoice->multicurrency_total_ttc, 'MT');
  1253. $multicurrency_amounts[$id] = price2num(-1 * $newvalue, 'MT');
  1254. } else {
  1255. $resteapayer = price2num($resteapayer, 'MT');
  1256. $amounts[$id] = $resteapayer;
  1257. // Multicurrency
  1258. $newvalue = price2num($this->invoice->multicurrency_total_ttc, 'MT');
  1259. $multicurrency_amounts[$id] = $newvalue;
  1260. }
  1261. // Creation of payment line
  1262. $paymentobj = new Paiement($this->db);
  1263. $paymentobj->datepaye = $datepaye;
  1264. $paymentobj->amounts = $amounts; // Array with all payments dispatching with invoice id
  1265. $paymentobj->multicurrency_amounts = $multicurrency_amounts; // Array with all payments dispatching
  1266. $paymentobj->paiementid = $paymentid;
  1267. $paymentobj->paiementcode = dol_getIdFromCode($this->db, $paymentid, 'c_paiement', 'id', 'code', 1);
  1268. $paymentobj->num_payment = $num_payment;
  1269. $paymentobj->note_private = $comment;
  1270. $payment_id = $paymentobj->create(DolibarrApiAccess::$user, ($closepaidinvoices == 'yes' ? 1 : 0)); // This include closing invoices
  1271. if ($payment_id < 0) {
  1272. $this->db->rollback();
  1273. throw new RestException(400, 'Payment error : '.$paymentobj->error);
  1274. }
  1275. if (isModEnabled("banque")) {
  1276. $label = '(CustomerInvoicePayment)';
  1277. if ($paymentobj->paiementcode == 'CHQ' && empty($chqemetteur)) {
  1278. throw new RestException(400, 'Emetteur is mandatory when payment code is '.$paymentobj->paiementcode);
  1279. }
  1280. if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
  1281. $label = '(CustomerInvoicePaymentBack)'; // Refund of a credit note
  1282. }
  1283. $result = $paymentobj->addPaymentToBank(DolibarrApiAccess::$user, 'payment', $label, $accountid, $chqemetteur, $chqbank);
  1284. if ($result < 0) {
  1285. $this->db->rollback();
  1286. throw new RestException(400, 'Add payment to bank error : '.$paymentobj->error);
  1287. }
  1288. }
  1289. $this->db->commit();
  1290. return $payment_id;
  1291. }
  1292. /**
  1293. * Add a payment to pay partially or completely one or several invoices.
  1294. * Warning: Take care that all invoices are owned by the same customer.
  1295. * Example of value for parameter arrayofamounts: {"1": {"amount": "99.99", "multicurrency_amount": ""}, "2": {"amount": "", "multicurrency_amount": "10"}}
  1296. *
  1297. * @param array $arrayofamounts {@from body} Array with id of invoices with amount to pay for each invoice
  1298. * @param string $datepaye {@from body} Payment date {@type timestamp}
  1299. * @param int $paymentid {@from body} Payment mode Id {@min 1}
  1300. * @param string $closepaidinvoices {@from body} Close paid invoices {@choice yes,no}
  1301. * @param int $accountid {@from body} Account Id {@min 1}
  1302. * @param string $num_payment {@from body} Payment number (optional)
  1303. * @param string $comment {@from body} Note private (optional)
  1304. * @param string $chqemetteur {@from body} Payment issuer (mandatory if paiementcode = 'CHQ')
  1305. * @param string $chqbank {@from body} Issuer bank name (optional)
  1306. * @param string $ref_ext {@from body} External reference (optional)
  1307. * @param bool $accepthigherpayment {@from body} Accept higher payments that it remains to be paid (optional)
  1308. *
  1309. * @url POST /paymentsdistributed
  1310. *
  1311. * @return int Payment ID
  1312. *
  1313. * @throws RestException 400
  1314. * @throws RestException 401
  1315. * @throws RestException 403
  1316. * @throws RestException 404
  1317. */
  1318. public function addPaymentDistributed($arrayofamounts, $datepaye, $paymentid, $closepaidinvoices, $accountid, $num_payment = '', $comment = '', $chqemetteur = '', $chqbank = '', $ref_ext = '', $accepthigherpayment = false)
  1319. {
  1320. require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
  1321. if (!DolibarrApiAccess::$user->rights->facture->creer) {
  1322. throw new RestException(403);
  1323. }
  1324. foreach ($arrayofamounts as $id => $amount) {
  1325. if (empty($id)) {
  1326. throw new RestException(400, 'Invoice ID is mandatory. Fill the invoice id and amount into arrayofamounts parameter. For example: {"1": "99.99", "2": "10"}');
  1327. }
  1328. if (!DolibarrApi::_checkAccessToResource('facture', $id)) {
  1329. throw new RestException(403, 'Access not allowed on invoice ID '.$id.' for login '.DolibarrApiAccess::$user->login);
  1330. }
  1331. }
  1332. if (isModEnabled("banque")) {
  1333. if (empty($accountid)) {
  1334. throw new RestException(400, 'Account ID is mandatory');
  1335. }
  1336. }
  1337. if (empty($paymentid)) {
  1338. throw new RestException(400, 'Payment ID or Payment Code is mandatory');
  1339. }
  1340. $this->db->begin();
  1341. $amounts = array();
  1342. $multicurrency_amounts = array();
  1343. // Loop on each invoice to pay
  1344. foreach ($arrayofamounts as $id => $amountarray) {
  1345. $result = $this->invoice->fetch($id);
  1346. if (!$result) {
  1347. $this->db->rollback();
  1348. throw new RestException(404, 'Invoice ID '.$id.' not found');
  1349. }
  1350. if (($amountarray["amount"] == "remain" || $amountarray["amount"] > 0) && ($amountarray["multicurrency_amount"] == "remain" || $amountarray["multicurrency_amount"] > 0)) {
  1351. $this->db->rollback();
  1352. throw new RestException(400, 'Payment in both currency '.$id.' ( amount: '.$amountarray["amount"].', multicurrency_amount: '.$amountarray["multicurrency_amount"].')');
  1353. }
  1354. $is_multicurrency = 0;
  1355. $total_ttc = $this->invoice->total_ttc;
  1356. if ($amountarray["multicurrency_amount"] > 0 || $amountarray["multicurrency_amount"] == "remain") {
  1357. $is_multicurrency = 1;
  1358. $total_ttc = $this->invoice->multicurrency_total_ttc;
  1359. }
  1360. // Calculate amount to pay
  1361. $totalpaid = $this->invoice->getSommePaiement($is_multicurrency);
  1362. $totalcreditnotes = $this->invoice->getSumCreditNotesUsed($is_multicurrency);
  1363. $totaldeposits = $this->invoice->getSumDepositsUsed($is_multicurrency);
  1364. $remainstopay = $amount = price2num($total_ttc - $totalpaid - $totalcreditnotes - $totaldeposits, 'MT');
  1365. if (!$is_multicurrency && $amountarray["amount"] != 'remain') {
  1366. $amount = price2num($amountarray["amount"], 'MT');
  1367. }
  1368. if ($is_multicurrency && $amountarray["multicurrency_amount"] != 'remain') {
  1369. $amount = price2num($amountarray["multicurrency_amount"], 'MT');
  1370. }
  1371. if ($amount > $remainstopay && !$accepthigherpayment) {
  1372. $this->db->rollback();
  1373. throw new RestException(400, 'Payment amount on invoice ID '.$id.' ('.$amount.') is higher than remain to pay ('.$remainstopay.')');
  1374. }
  1375. if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
  1376. $amount = price2num(-1 * $amount, 'MT');
  1377. }
  1378. if ($is_multicurrency) {
  1379. $amounts[$id] = null;
  1380. // Multicurrency
  1381. $multicurrency_amounts[$id] = $amount;
  1382. } else {
  1383. $amounts[$id] = $amount;
  1384. // Multicurrency
  1385. $multicurrency_amounts[$id] = null;
  1386. }
  1387. }
  1388. // Creation of payment line
  1389. $paymentobj = new Paiement($this->db);
  1390. $paymentobj->datepaye = $datepaye;
  1391. $paymentobj->amounts = $amounts; // Array with all payments dispatching with invoice id
  1392. $paymentobj->multicurrency_amounts = $multicurrency_amounts; // Array with all payments dispatching
  1393. $paymentobj->paiementid = $paymentid;
  1394. $paymentobj->paiementcode = dol_getIdFromCode($this->db, $paymentid, 'c_paiement', 'id', 'code', 1);
  1395. $paymentobj->num_payment = $num_payment;
  1396. $paymentobj->note_private = $comment;
  1397. $paymentobj->ref_ext = $ref_ext;
  1398. $payment_id = $paymentobj->create(DolibarrApiAccess::$user, ($closepaidinvoices == 'yes' ? 1 : 0)); // This include closing invoices
  1399. if ($payment_id < 0) {
  1400. $this->db->rollback();
  1401. throw new RestException(400, 'Payment error : '.$paymentobj->error);
  1402. }
  1403. if (isModEnabled("banque")) {
  1404. $label = '(CustomerInvoicePayment)';
  1405. if ($paymentobj->paiementcode == 'CHQ' && empty($chqemetteur)) {
  1406. throw new RestException(400, 'Emetteur is mandatory when payment code is '.$paymentobj->paiementcode);
  1407. }
  1408. if ($this->invoice->type == Facture::TYPE_CREDIT_NOTE) {
  1409. $label = '(CustomerInvoicePaymentBack)'; // Refund of a credit note
  1410. }
  1411. $result = $paymentobj->addPaymentToBank(DolibarrApiAccess::$user, 'payment', $label, $accountid, $chqemetteur, $chqbank);
  1412. if ($result < 0) {
  1413. $this->db->rollback();
  1414. throw new RestException(400, 'Add payment to bank error : '.$paymentobj->error);
  1415. }
  1416. }
  1417. $this->db->commit();
  1418. return $payment_id;
  1419. }
  1420. /**
  1421. * Update a payment
  1422. *
  1423. * @param int $id Id of payment
  1424. * @param string $num_payment Payment number
  1425. *
  1426. * @url PUT payments/{id}
  1427. *
  1428. * @return array
  1429. *
  1430. * @throws RestException 400 Bad parameters
  1431. * @throws RestException 401 Not allowed
  1432. * @throws RestException 404 Not found
  1433. */
  1434. public function putPayment($id, $num_payment = '')
  1435. {
  1436. require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
  1437. if (!DolibarrApiAccess::$user->rights->facture->creer) {
  1438. throw new RestException(401);
  1439. }
  1440. if (empty($id)) {
  1441. throw new RestException(400, 'Payment ID is mandatory');
  1442. }
  1443. $paymentobj = new Paiement($this->db);
  1444. $result = $paymentobj->fetch($id);
  1445. if (!$result) {
  1446. throw new RestException(404, 'Payment not found');
  1447. }
  1448. if (!empty($num_payment)) {
  1449. $result = $paymentobj->update_num($num_payment);
  1450. if ($result < 0) {
  1451. throw new RestException(500, 'Error when updating the payment num');
  1452. }
  1453. }
  1454. return [
  1455. 'success' => [
  1456. 'code' => 200,
  1457. 'message' => 'Payment updated'
  1458. ]
  1459. ];
  1460. }
  1461. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
  1462. /**
  1463. * Clean sensible object datas
  1464. *
  1465. * @param Object $object Object to clean
  1466. * @return Object Object with cleaned properties
  1467. */
  1468. protected function _cleanObjectDatas($object)
  1469. {
  1470. // phpcs:enable
  1471. $object = parent::_cleanObjectDatas($object);
  1472. unset($object->note);
  1473. unset($object->address);
  1474. unset($object->barcode_type);
  1475. unset($object->barcode_type_code);
  1476. unset($object->barcode_type_label);
  1477. unset($object->barcode_type_coder);
  1478. unset($object->canvas);
  1479. return $object;
  1480. }
  1481. /**
  1482. * Validate fields before create or update object
  1483. *
  1484. * @param array|null $data Datas to validate
  1485. * @return array
  1486. *
  1487. * @throws RestException
  1488. */
  1489. private function _validate($data)
  1490. {
  1491. $invoice = array();
  1492. foreach (Invoices::$FIELDS as $field) {
  1493. if (!isset($data[$field])) {
  1494. throw new RestException(400, "$field field missing");
  1495. }
  1496. $invoice[$field] = $data[$field];
  1497. }
  1498. return $invoice;
  1499. }
  1500. /**
  1501. * Get properties of a template invoice object
  1502. *
  1503. * Return an array with invoice informations
  1504. *
  1505. * @param int $id ID of template invoice
  1506. * @param int $contact_list 0:Return array contains all properties, 1:Return array contains just id, -1: Do not return contacts/adddesses
  1507. * @return Object Object with cleaned properties
  1508. *
  1509. * @url GET templates/{id}
  1510. *
  1511. * @throws RestException
  1512. */
  1513. public function getTemplateInvoice($id, $contact_list = 1)
  1514. {
  1515. return $this->_fetchTemplateInvoice($id, '', '', $contact_list);
  1516. }
  1517. /**
  1518. * Get properties of an invoice object
  1519. *
  1520. * Return an array with invoice informations
  1521. *
  1522. * @param int $id ID of order
  1523. * @param string $ref Ref of object
  1524. * @param string $ref_ext External reference of object
  1525. * @param int $contact_list 0: Returned array of contacts/addresses contains all properties, 1: Return array contains just id, -1: Do not return contacts/adddesses
  1526. * @return Object Object with cleaned properties
  1527. *
  1528. * @throws RestException
  1529. */
  1530. private function _fetchTemplateInvoice($id, $ref = '', $ref_ext = '', $contact_list = 1)
  1531. {
  1532. if (!DolibarrApiAccess::$user->hasRight('facture', 'lire')) {
  1533. throw new RestException(401);
  1534. }
  1535. $result = $this->template_invoice->fetch($id, $ref, $ref_ext);
  1536. if (!$result) {
  1537. throw new RestException(404, 'Template invoice not found');
  1538. }
  1539. if (!DolibarrApi::_checkAccessToResource('facturerec', $this->template_invoice->id)) {
  1540. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  1541. }
  1542. // Add external contacts ids
  1543. if ($contact_list > -1) {
  1544. $tmparray = $this->template_invoice->liste_contact(-1, 'external', $contact_list);
  1545. if (is_array($tmparray)) {
  1546. $this->template_invoice->contacts_ids = $tmparray;
  1547. }
  1548. }
  1549. $this->template_invoice->fetchObjectLinked();
  1550. return $this->_cleanTemplateObjectDatas($this->template_invoice);
  1551. }
  1552. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
  1553. /**
  1554. * Clean sensible object datas
  1555. *
  1556. * @param Object $object Object to clean
  1557. * @return Object Object with cleaned properties
  1558. */
  1559. protected function _cleanTemplateObjectDatas($object)
  1560. {
  1561. // phpcs:enable
  1562. $object = parent::_cleanObjectDatas($object);
  1563. unset($object->note);
  1564. unset($object->address);
  1565. unset($object->barcode_type);
  1566. unset($object->barcode_type_code);
  1567. unset($object->barcode_type_label);
  1568. unset($object->barcode_type_coder);
  1569. unset($object->canvas);
  1570. return $object;
  1571. }
  1572. }