api_orders.class.php 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126
  1. <?php
  2. /* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
  3. * Copyright (C) 2016 Laurent Destailleur <eldy@users.sourceforge.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.'/commande/class/commande.class.php';
  20. /**
  21. * API class for orders
  22. *
  23. * @access protected
  24. * @class DolibarrApiAccess {@requires user,external}
  25. */
  26. class Orders extends DolibarrApi
  27. {
  28. /**
  29. * @var array $FIELDS Mandatory fields, checked when create and update object
  30. */
  31. public static $FIELDS = array(
  32. 'socid',
  33. 'date'
  34. );
  35. /**
  36. * @var Commande $commande {@type Commande}
  37. */
  38. public $commande;
  39. /**
  40. * Constructor
  41. */
  42. public function __construct()
  43. {
  44. global $db, $conf;
  45. $this->db = $db;
  46. $this->commande = new Commande($this->db);
  47. }
  48. /**
  49. * Get properties of an order object by id
  50. *
  51. * Return an array with order informations
  52. *
  53. * @param int $id ID of order
  54. * @param int $contact_list 0: Returned array of contacts/addresses contains all properties, 1: Return array contains just id
  55. * @return array|mixed data without useless information
  56. *
  57. * @throws RestException
  58. */
  59. public function get($id, $contact_list = 1)
  60. {
  61. return $this->_fetch($id, '', '', $contact_list);
  62. }
  63. /**
  64. * Get properties of an order object by ref
  65. *
  66. * Return an array with order informations
  67. *
  68. * @param string $ref Ref of object
  69. * @param int $contact_list 0: Returned array of contacts/addresses contains all properties, 1: Return array contains just id
  70. * @return array|mixed data without useless information
  71. *
  72. * @url GET ref/{ref}
  73. *
  74. * @throws RestException
  75. */
  76. public function getByRef($ref, $contact_list = 1)
  77. {
  78. return $this->_fetch('', $ref, '', $contact_list);
  79. }
  80. /**
  81. * Get properties of an order object by ref_ext
  82. *
  83. * Return an array with order informations
  84. *
  85. * @param string $ref_ext External reference of object
  86. * @param int $contact_list 0: Returned array of contacts/addresses contains all properties, 1: Return array contains just id
  87. * @return array|mixed data without useless information
  88. *
  89. * @url GET ref_ext/{ref_ext}
  90. *
  91. * @throws RestException
  92. */
  93. public function getByRefExt($ref_ext, $contact_list = 1)
  94. {
  95. return $this->_fetch('', '', $ref_ext, $contact_list);
  96. }
  97. /**
  98. * Get properties of an order object
  99. *
  100. * Return an array with order informations
  101. *
  102. * @param int $id ID of order
  103. * @param string $ref Ref of object
  104. * @param string $ref_ext External reference of object
  105. * @param int $contact_list 0: Returned array of contacts/addresses contains all properties, 1: Return array contains just id
  106. * @return Object Object with cleaned properties
  107. *
  108. * @throws RestException
  109. */
  110. private function _fetch($id, $ref = '', $ref_ext = '', $contact_list = 1)
  111. {
  112. if (!DolibarrApiAccess::$user->hasRight('commande', 'lire')) {
  113. throw new RestException(401);
  114. }
  115. $result = $this->commande->fetch($id, $ref, $ref_ext);
  116. if (!$result) {
  117. throw new RestException(404, 'Order not found');
  118. }
  119. if (!DolibarrApi::_checkAccessToResource('commande', $this->commande->id)) {
  120. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  121. }
  122. // Add external contacts ids
  123. $tmparray = $this->commande->liste_contact(-1, 'external', $contact_list);
  124. if (is_array($tmparray)) {
  125. $this->commande->contacts_ids = $tmparray;
  126. }
  127. $this->commande->fetchObjectLinked();
  128. // Add online_payment_url, cf #20477
  129. require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
  130. $this->commande->online_payment_url = getOnlinePaymentUrl(0, 'order', $this->commande->ref);
  131. return $this->_cleanObjectDatas($this->commande);
  132. }
  133. /**
  134. * List orders
  135. *
  136. * Get a list of orders
  137. *
  138. * @param string $sortfield Sort field
  139. * @param string $sortorder Sort order
  140. * @param int $limit Limit for list
  141. * @param int $page Page number
  142. * @param string $thirdparty_ids Thirdparty ids to filter orders of (example '1' or '1,2,3') {@pattern /^[0-9,]*$/i}
  143. * @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.ref:like:'SO-%') and (t.date_creation:<:'20160101')"
  144. * @param string $sqlfilterlines Other criteria to filter answers separated by a comma. Syntax example "(tl.fk_product:=:'17') and (tl.price:<:'250')"
  145. * @param string $properties Restrict the data returned to theses properties. Ignored if empty. Comma separated list of properties names
  146. * @return array Array of order objects
  147. *
  148. * @throws RestException 404 Not found
  149. * @throws RestException 503 Error
  150. */
  151. public function index($sortfield = "t.rowid", $sortorder = 'ASC', $limit = 100, $page = 0, $thirdparty_ids = '', $sqlfilters = '', $sqlfilterlines = '', $properties = '')
  152. {
  153. if (!DolibarrApiAccess::$user->hasRight('commande', 'lire')) {
  154. throw new RestException(401);
  155. }
  156. $obj_ret = array();
  157. // case of external user, $thirdparty_ids param is ignored and replaced by user's socid
  158. $socids = DolibarrApiAccess::$user->socid ? DolibarrApiAccess::$user->socid : $thirdparty_ids;
  159. // If the internal user must only see his customers, force searching by him
  160. $search_sale = 0;
  161. if (!DolibarrApiAccess::$user->rights->societe->client->voir && !$socids) {
  162. $search_sale = DolibarrApiAccess::$user->id;
  163. }
  164. $sql = "SELECT t.rowid";
  165. if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socids) || $search_sale > 0) {
  166. $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)
  167. }
  168. $sql .= " FROM ".MAIN_DB_PREFIX."commande AS t LEFT JOIN ".MAIN_DB_PREFIX."commande_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
  169. if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socids) || $search_sale > 0) {
  170. $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc"; // We need this table joined to the select in order to filter by sale
  171. }
  172. $sql .= ' WHERE t.entity IN ('.getEntity('commande').')';
  173. if ((!DolibarrApiAccess::$user->rights->societe->client->voir && !$socids) || $search_sale > 0) {
  174. $sql .= " AND t.fk_soc = sc.fk_soc";
  175. }
  176. if ($socids) {
  177. $sql .= " AND t.fk_soc IN (".$this->db->sanitize($socids).")";
  178. }
  179. if ($search_sale > 0) {
  180. $sql .= " AND t.rowid = sc.fk_soc"; // Join for the needed table to filter by sale
  181. }
  182. // Insert sale filter
  183. if ($search_sale > 0) {
  184. $sql .= " AND sc.fk_user = ".((int) $search_sale);
  185. }
  186. // Add sql filters
  187. if ($sqlfilters) {
  188. $errormessage = '';
  189. $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
  190. if ($errormessage) {
  191. throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage);
  192. }
  193. }
  194. // Add sql filters for lines
  195. if ($sqlfilterlines) {
  196. $errormessage = '';
  197. $sql .= " AND EXISTS (SELECT tl.rowid FROM ".MAIN_DB_PREFIX."commandedet AS tl WHERE tl.fk_commande = t.rowid";
  198. $sql .= forgeSQLFromUniversalSearchCriteria($sqlfilterlines, $errormessage);
  199. $sql .= ")";
  200. if ($errormessage) {
  201. throw new RestException(400, 'Error when validating parameter sqlfilterlines -> '.$errormessage);
  202. }
  203. }
  204. $sql .= $this->db->order($sortfield, $sortorder);
  205. if ($limit) {
  206. if ($page < 0) {
  207. $page = 0;
  208. }
  209. $offset = $limit * $page;
  210. $sql .= $this->db->plimit($limit + 1, $offset);
  211. }
  212. dol_syslog("API Rest request");
  213. $result = $this->db->query($sql);
  214. if ($result) {
  215. $num = $this->db->num_rows($result);
  216. $min = min($num, ($limit <= 0 ? $num : $limit));
  217. $i = 0;
  218. while ($i < $min) {
  219. $obj = $this->db->fetch_object($result);
  220. $commande_static = new Commande($this->db);
  221. if ($commande_static->fetch($obj->rowid)) {
  222. // Add external contacts ids
  223. $tmparray = $commande_static->liste_contact(-1, 'external', 1);
  224. if (is_array($tmparray)) {
  225. $commande_static->contacts_ids = $tmparray;
  226. }
  227. // Add online_payment_url, cf #20477
  228. require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
  229. $commande_static->online_payment_url = getOnlinePaymentUrl(0, 'order', $commande_static->ref);
  230. $obj_ret[] = $this->_filterObjectProperties($this->_cleanObjectDatas($commande_static), $properties);
  231. }
  232. $i++;
  233. }
  234. } else {
  235. throw new RestException(503, 'Error when retrieve commande list : '.$this->db->lasterror());
  236. }
  237. return $obj_ret;
  238. }
  239. /**
  240. * Create a sale order
  241. *
  242. * Exemple: { "socid": 2, "date": 1595196000, "type": 0, "lines": [{ "fk_product": 2, "qty": 1 }] }
  243. *
  244. * @param array $request_data Request data
  245. * @return int ID of order
  246. */
  247. public function post($request_data = null)
  248. {
  249. if (!DolibarrApiAccess::$user->rights->commande->creer) {
  250. throw new RestException(401, "Insuffisant rights");
  251. }
  252. // Check mandatory fields
  253. $result = $this->_validate($request_data);
  254. foreach ($request_data as $field => $value) {
  255. if ($field === 'caller') {
  256. // 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
  257. $this->commande->context['caller'] = $request_data['caller'];
  258. continue;
  259. }
  260. $this->commande->$field = $value;
  261. }
  262. /*if (isset($request_data["lines"])) {
  263. $lines = array();
  264. foreach ($request_data["lines"] as $line) {
  265. array_push($lines, (object) $line);
  266. }
  267. $this->commande->lines = $lines;
  268. }*/
  269. if ($this->commande->create(DolibarrApiAccess::$user) < 0) {
  270. throw new RestException(500, "Error creating order", array_merge(array($this->commande->error), $this->commande->errors));
  271. }
  272. return ((int) $this->commande->id);
  273. }
  274. /**
  275. * Get lines of an order
  276. *
  277. * @param int $id Id of order
  278. *
  279. * @url GET {id}/lines
  280. *
  281. * @return array
  282. */
  283. public function getLines($id)
  284. {
  285. if (!DolibarrApiAccess::$user->hasRight('commande', 'lire')) {
  286. throw new RestException(401);
  287. }
  288. $result = $this->commande->fetch($id);
  289. if (!$result) {
  290. throw new RestException(404, 'Order not found');
  291. }
  292. if (!DolibarrApi::_checkAccessToResource('commande', $this->commande->id)) {
  293. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  294. }
  295. $this->commande->getLinesArray();
  296. $result = array();
  297. foreach ($this->commande->lines as $line) {
  298. array_push($result, $this->_cleanObjectDatas($line));
  299. }
  300. return $result;
  301. }
  302. /**
  303. * Add a line to given order
  304. *
  305. * @param int $id Id of order to update
  306. * @param array $request_data OrderLine data
  307. *
  308. * @url POST {id}/lines
  309. *
  310. * @return int
  311. */
  312. public function postLine($id, $request_data = null)
  313. {
  314. if (!DolibarrApiAccess::$user->rights->commande->creer) {
  315. throw new RestException(401);
  316. }
  317. $result = $this->commande->fetch($id);
  318. if (!$result) {
  319. throw new RestException(404, 'Order not found');
  320. }
  321. if (!DolibarrApi::_checkAccessToResource('commande', $this->commande->id)) {
  322. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  323. }
  324. $request_data = (object) $request_data;
  325. $request_data->desc = sanitizeVal($request_data->desc, 'restricthtml');
  326. $request_data->label = sanitizeVal($request_data->label);
  327. $updateRes = $this->commande->addline(
  328. $request_data->desc,
  329. $request_data->subprice,
  330. $request_data->qty,
  331. $request_data->tva_tx,
  332. $request_data->localtax1_tx,
  333. $request_data->localtax2_tx,
  334. $request_data->fk_product,
  335. $request_data->remise_percent,
  336. $request_data->info_bits,
  337. $request_data->fk_remise_except,
  338. $request_data->price_base_type ? $request_data->price_base_type : 'HT',
  339. $request_data->subprice,
  340. $request_data->date_start,
  341. $request_data->date_end,
  342. $request_data->product_type,
  343. $request_data->rang,
  344. $request_data->special_code,
  345. $request_data->fk_parent_line,
  346. $request_data->fk_fournprice,
  347. $request_data->pa_ht,
  348. $request_data->label,
  349. $request_data->array_options,
  350. $request_data->fk_unit,
  351. $request_data->origin,
  352. $request_data->origin_id,
  353. $request_data->multicurrency_subprice,
  354. $request_data->ref_ext
  355. );
  356. if ($updateRes > 0) {
  357. return $updateRes;
  358. } else {
  359. throw new RestException(400, $this->commande->error);
  360. }
  361. }
  362. /**
  363. * Update a line to given order
  364. *
  365. * @param int $id Id of order to update
  366. * @param int $lineid Id of line to update
  367. * @param array $request_data OrderLine data
  368. * @return Object|false Object with cleaned properties
  369. *
  370. * @url PUT {id}/lines/{lineid}
  371. */
  372. public function putLine($id, $lineid, $request_data = null)
  373. {
  374. if (!DolibarrApiAccess::$user->rights->commande->creer) {
  375. throw new RestException(401);
  376. }
  377. $result = $this->commande->fetch($id);
  378. if (!$result) {
  379. throw new RestException(404, 'Order not found');
  380. }
  381. if (!DolibarrApi::_checkAccessToResource('commande', $this->commande->id)) {
  382. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  383. }
  384. $request_data = (object) $request_data;
  385. $request_data->desc = sanitizeVal($request_data->desc, 'restricthtml');
  386. $request_data->label = sanitizeVal($request_data->label);
  387. $updateRes = $this->commande->updateline(
  388. $lineid,
  389. $request_data->desc,
  390. $request_data->subprice,
  391. $request_data->qty,
  392. $request_data->remise_percent,
  393. $request_data->tva_tx,
  394. $request_data->localtax1_tx,
  395. $request_data->localtax2_tx,
  396. $request_data->price_base_type ? $request_data->price_base_type : 'HT',
  397. $request_data->info_bits,
  398. $request_data->date_start,
  399. $request_data->date_end,
  400. $request_data->product_type,
  401. $request_data->fk_parent_line,
  402. 0,
  403. $request_data->fk_fournprice,
  404. $request_data->pa_ht,
  405. $request_data->label,
  406. $request_data->special_code,
  407. $request_data->array_options,
  408. $request_data->fk_unit,
  409. $request_data->multicurrency_subprice,
  410. 0,
  411. $request_data->ref_ext,
  412. $request_data->rang
  413. );
  414. if ($updateRes > 0) {
  415. $result = $this->get($id);
  416. unset($result->line);
  417. return $this->_cleanObjectDatas($result);
  418. }
  419. return false;
  420. }
  421. /**
  422. * Delete a line of a given order
  423. *
  424. * @param int $id Id of order to update
  425. * @param int $lineid Id of line to delete
  426. * @return Object Object with cleaned properties
  427. *
  428. * @url DELETE {id}/lines/{lineid}
  429. *
  430. * @throws RestException 401
  431. * @throws RestException 404
  432. */
  433. public function deleteLine($id, $lineid)
  434. {
  435. if (!DolibarrApiAccess::$user->rights->commande->creer) {
  436. throw new RestException(401);
  437. }
  438. $result = $this->commande->fetch($id);
  439. if (!$result) {
  440. throw new RestException(404, 'Order not found');
  441. }
  442. if (!DolibarrApi::_checkAccessToResource('commande', $this->commande->id)) {
  443. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  444. }
  445. $updateRes = $this->commande->deleteline(DolibarrApiAccess::$user, $lineid, $id);
  446. if ($updateRes > 0) {
  447. return $this->get($id);
  448. } else {
  449. throw new RestException(405, $this->commande->error);
  450. }
  451. }
  452. /**
  453. * Get contacts of given order
  454. *
  455. * Return an array with contact informations
  456. *
  457. * @param int $id ID of order
  458. * @param string $type Type of the contact (BILLING, SHIPPING, CUSTOMER)
  459. * @return Object Object with cleaned properties
  460. *
  461. * @url GET {id}/contacts
  462. *
  463. * @throws RestException
  464. */
  465. public function getContacts($id, $type = '')
  466. {
  467. if (!DolibarrApiAccess::$user->hasRight('commande', 'lire')) {
  468. throw new RestException(401);
  469. }
  470. $result = $this->commande->fetch($id);
  471. if (!$result) {
  472. throw new RestException(404, 'Order not found');
  473. }
  474. if (!DolibarrApi::_checkAccessToResource('commande', $this->commande->id)) {
  475. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  476. }
  477. $contacts = $this->commande->liste_contact(-1, 'external', 0, $type);
  478. return $this->_cleanObjectDatas($contacts);
  479. }
  480. /**
  481. * Add a contact type of given order
  482. *
  483. * @param int $id Id of order to update
  484. * @param int $contactid Id of contact to add
  485. * @param string $type Type of the contact (BILLING, SHIPPING, CUSTOMER)
  486. * @return array
  487. *
  488. * @url POST {id}/contact/{contactid}/{type}
  489. *
  490. * @throws RestException 401
  491. * @throws RestException 404
  492. */
  493. public function postContact($id, $contactid, $type)
  494. {
  495. if (!DolibarrApiAccess::$user->rights->commande->creer) {
  496. throw new RestException(401);
  497. }
  498. $result = $this->commande->fetch($id);
  499. if (!$result) {
  500. throw new RestException(404, 'Order not found');
  501. }
  502. if (!DolibarrApi::_checkAccessToResource('commande', $this->commande->id)) {
  503. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  504. }
  505. $result = $this->commande->add_contact($contactid, $type, 'external');
  506. if ($result < 0) {
  507. throw new RestException(500, 'Error when added the contact');
  508. }
  509. if ($result == 0) {
  510. throw new RestException(304, 'contact already added');
  511. }
  512. return array(
  513. 'success' => array(
  514. 'code' => 200,
  515. 'message' => 'Contact linked to the order'
  516. )
  517. );
  518. }
  519. /**
  520. * Unlink a contact type of given order
  521. *
  522. * @param int $id Id of order to update
  523. * @param int $contactid Id of contact
  524. * @param string $type Type of the contact (BILLING, SHIPPING, CUSTOMER).
  525. *
  526. * @url DELETE {id}/contact/{contactid}/{type}
  527. *
  528. * @return array
  529. *
  530. * @throws RestException 401
  531. * @throws RestException 404
  532. * @throws RestException 500 System error
  533. */
  534. public function deleteContact($id, $contactid, $type)
  535. {
  536. if (!DolibarrApiAccess::$user->rights->commande->creer) {
  537. throw new RestException(401);
  538. }
  539. $result = $this->commande->fetch($id);
  540. if (!$result) {
  541. throw new RestException(404, 'Order not found');
  542. }
  543. if (!DolibarrApi::_checkAccessToResource('commande', $this->commande->id)) {
  544. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  545. }
  546. $contacts = $this->commande->liste_contact();
  547. foreach ($contacts as $contact) {
  548. if ($contact['id'] == $contactid && $contact['code'] == $type) {
  549. $result = $this->commande->delete_contact($contact['rowid']);
  550. if (!$result) {
  551. throw new RestException(500, 'Error when deleted the contact');
  552. }
  553. }
  554. }
  555. return array(
  556. 'success' => array(
  557. 'code' => 200,
  558. 'message' => 'Contact unlinked from order'
  559. )
  560. );
  561. }
  562. /**
  563. * Update order general fields (won't touch lines of order)
  564. *
  565. * @param int $id Id of order to update
  566. * @param array $request_data Datas
  567. * @return Object Object with cleaned properties
  568. */
  569. public function put($id, $request_data = null)
  570. {
  571. if (!DolibarrApiAccess::$user->rights->commande->creer) {
  572. throw new RestException(401);
  573. }
  574. $result = $this->commande->fetch($id);
  575. if (!$result) {
  576. throw new RestException(404, 'Order not found');
  577. }
  578. if (!DolibarrApi::_checkAccessToResource('commande', $this->commande->id)) {
  579. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  580. }
  581. foreach ($request_data as $field => $value) {
  582. if ($field == 'id') {
  583. continue;
  584. }
  585. if ($field === 'caller') {
  586. // 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
  587. $this->commande->context['caller'] = $request_data['caller'];
  588. continue;
  589. }
  590. $this->commande->$field = $value;
  591. }
  592. // Update availability
  593. if (!empty($this->commande->availability_id)) {
  594. if ($this->commande->availability($this->commande->availability_id) < 0) {
  595. throw new RestException(400, 'Error while updating availability');
  596. }
  597. }
  598. if ($this->commande->update(DolibarrApiAccess::$user) > 0) {
  599. return $this->get($id);
  600. } else {
  601. throw new RestException(500, $this->commande->error);
  602. }
  603. }
  604. /**
  605. * Delete order
  606. *
  607. * @param int $id Order ID
  608. * @return array
  609. */
  610. public function delete($id)
  611. {
  612. if (!DolibarrApiAccess::$user->rights->commande->supprimer) {
  613. throw new RestException(401);
  614. }
  615. $result = $this->commande->fetch($id);
  616. if (!$result) {
  617. throw new RestException(404, 'Order not found');
  618. }
  619. if (!DolibarrApi::_checkAccessToResource('commande', $this->commande->id)) {
  620. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  621. }
  622. if (!$this->commande->delete(DolibarrApiAccess::$user)) {
  623. throw new RestException(500, 'Error when deleting order : '.$this->commande->error);
  624. }
  625. return array(
  626. 'success' => array(
  627. 'code' => 200,
  628. 'message' => 'Order deleted'
  629. )
  630. );
  631. }
  632. /**
  633. * Validate an order
  634. *
  635. * If you get a bad value for param notrigger check, provide this in body
  636. * {
  637. * "idwarehouse": 0,
  638. * "notrigger": 0
  639. * }
  640. *
  641. * @param int $id Order ID
  642. * @param int $idwarehouse Warehouse ID
  643. * @param int $notrigger 1=Does not execute triggers, 0= execute triggers
  644. * @return Object Object with cleaned properties
  645. *
  646. * @url POST {id}/validate
  647. *
  648. * @throws RestException 304
  649. * @throws RestException 401
  650. * @throws RestException 404
  651. * @throws RestException 500 System error
  652. *
  653. */
  654. public function validate($id, $idwarehouse = 0, $notrigger = 0)
  655. {
  656. if (!DolibarrApiAccess::$user->rights->commande->creer) {
  657. throw new RestException(401);
  658. }
  659. $result = $this->commande->fetch($id);
  660. if (!$result) {
  661. throw new RestException(404, 'Order not found');
  662. }
  663. $result = $this->commande->fetch_thirdparty(); // do not check result, as failure is not fatal (used only for mail notification substitutes)
  664. if (!DolibarrApi::_checkAccessToResource('commande', $this->commande->id)) {
  665. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  666. }
  667. $result = $this->commande->valid(DolibarrApiAccess::$user, $idwarehouse, $notrigger);
  668. if ($result == 0) {
  669. throw new RestException(304, 'Error nothing done. May be object is already validated');
  670. }
  671. if ($result < 0) {
  672. throw new RestException(500, 'Error when validating Order: '.$this->commande->error);
  673. }
  674. $result = $this->commande->fetch($id);
  675. $this->commande->fetchObjectLinked();
  676. //fix #20477 : add online_payment_url
  677. require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
  678. $this->commande->online_payment_url = getOnlinePaymentUrl(0, 'order', $this->commande->ref);
  679. return $this->_cleanObjectDatas($this->commande);
  680. }
  681. /**
  682. * Tag the order as validated (opened)
  683. *
  684. * Function used when order is reopend after being closed.
  685. *
  686. * @param int $id Id of the order
  687. *
  688. * @url POST {id}/reopen
  689. *
  690. * @return int
  691. *
  692. * @throws RestException 304
  693. * @throws RestException 400
  694. * @throws RestException 401
  695. * @throws RestException 404
  696. * @throws RestException 405
  697. */
  698. public function reopen($id)
  699. {
  700. if (!DolibarrApiAccess::$user->rights->commande->creer) {
  701. throw new RestException(401);
  702. }
  703. if (empty($id)) {
  704. throw new RestException(400, 'Order ID is mandatory');
  705. }
  706. $result = $this->commande->fetch($id);
  707. if (!$result) {
  708. throw new RestException(404, 'Order not found');
  709. }
  710. $result = $this->commande->set_reopen(DolibarrApiAccess::$user);
  711. if ($result < 0) {
  712. throw new RestException(405, $this->commande->error);
  713. } elseif ($result == 0) {
  714. throw new RestException(304);
  715. }
  716. return $result;
  717. }
  718. /**
  719. * Classify the order as invoiced. Could be also called setbilled
  720. *
  721. * @param int $id Id of the order
  722. * @return Object Object with cleaned properties
  723. *
  724. * @url POST {id}/setinvoiced
  725. *
  726. * @throws RestException 400
  727. * @throws RestException 401
  728. * @throws RestException 404
  729. * @throws RestException 405
  730. */
  731. public function setinvoiced($id)
  732. {
  733. if (!DolibarrApiAccess::$user->rights->commande->creer) {
  734. throw new RestException(401);
  735. }
  736. if (empty($id)) {
  737. throw new RestException(400, 'Order ID is mandatory');
  738. }
  739. $result = $this->commande->fetch($id);
  740. if (!$result) {
  741. throw new RestException(404, 'Order not found');
  742. }
  743. $result = $this->commande->classifyBilled(DolibarrApiAccess::$user);
  744. if ($result < 0) {
  745. throw new RestException(400, $this->commande->error);
  746. }
  747. $result = $this->commande->fetch($id);
  748. if (!$result) {
  749. throw new RestException(404, 'Order not found');
  750. }
  751. if (!DolibarrApi::_checkAccessToResource('commande', $this->commande->id)) {
  752. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  753. }
  754. $this->commande->fetchObjectLinked();
  755. return $this->_cleanObjectDatas($this->commande);
  756. }
  757. /**
  758. * Close an order (Classify it as "Delivered")
  759. *
  760. * @param int $id Order ID
  761. * @param int $notrigger Disabled triggers
  762. * @return Object Object with cleaned properties
  763. *
  764. * @url POST {id}/close
  765. */
  766. public function close($id, $notrigger = 0)
  767. {
  768. if (!DolibarrApiAccess::$user->rights->commande->creer) {
  769. throw new RestException(401);
  770. }
  771. $result = $this->commande->fetch($id);
  772. if (!$result) {
  773. throw new RestException(404, 'Order not found');
  774. }
  775. if (!DolibarrApi::_checkAccessToResource('commande', $this->commande->id)) {
  776. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  777. }
  778. $result = $this->commande->cloture(DolibarrApiAccess::$user, $notrigger);
  779. if ($result == 0) {
  780. throw new RestException(304, 'Error nothing done. May be object is already closed');
  781. }
  782. if ($result < 0) {
  783. throw new RestException(500, 'Error when closing Order: '.$this->commande->error);
  784. }
  785. $result = $this->commande->fetch($id);
  786. if (!$result) {
  787. throw new RestException(404, 'Order not found');
  788. }
  789. if (!DolibarrApi::_checkAccessToResource('commande', $this->commande->id)) {
  790. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  791. }
  792. $this->commande->fetchObjectLinked();
  793. return $this->_cleanObjectDatas($this->commande);
  794. }
  795. /**
  796. * Set an order to draft
  797. *
  798. * @param int $id Order ID
  799. * @param int $idwarehouse Warehouse ID to use for stock change (Used only if option STOCK_CALCULATE_ON_VALIDATE_ORDER is on)
  800. * @return Object Object with cleaned properties
  801. *
  802. * @url POST {id}/settodraft
  803. */
  804. public function settodraft($id, $idwarehouse = -1)
  805. {
  806. if (!DolibarrApiAccess::$user->rights->commande->creer) {
  807. throw new RestException(401);
  808. }
  809. $result = $this->commande->fetch($id);
  810. if (!$result) {
  811. throw new RestException(404, 'Order not found');
  812. }
  813. if (!DolibarrApi::_checkAccessToResource('commande', $this->commande->id)) {
  814. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  815. }
  816. $result = $this->commande->setDraft(DolibarrApiAccess::$user, $idwarehouse);
  817. if ($result == 0) {
  818. throw new RestException(304, 'Nothing done. May be object is already closed');
  819. }
  820. if ($result < 0) {
  821. throw new RestException(500, 'Error when closing Order: '.$this->commande->error);
  822. }
  823. $result = $this->commande->fetch($id);
  824. if (!$result) {
  825. throw new RestException(404, 'Order not found');
  826. }
  827. if (!DolibarrApi::_checkAccessToResource('commande', $this->commande->id)) {
  828. throw new RestException(401, 'Access not allowed for login '.DolibarrApiAccess::$user->login);
  829. }
  830. $this->commande->fetchObjectLinked();
  831. return $this->_cleanObjectDatas($this->commande);
  832. }
  833. /**
  834. * Create an order using an existing proposal.
  835. *
  836. * @param int $proposalid Id of the proposal
  837. * @return Object Object with cleaned properties
  838. *
  839. * @url POST /createfromproposal/{proposalid}
  840. *
  841. * @throws RestException 400
  842. * @throws RestException 401
  843. * @throws RestException 404
  844. * @throws RestException 405
  845. */
  846. public function createOrderFromProposal($proposalid)
  847. {
  848. require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php';
  849. if (!DolibarrApiAccess::$user->hasRight('propal', 'lire')) {
  850. throw new RestException(401);
  851. }
  852. if (!DolibarrApiAccess::$user->rights->commande->creer) {
  853. throw new RestException(401);
  854. }
  855. if (empty($proposalid)) {
  856. throw new RestException(400, 'Proposal ID is mandatory');
  857. }
  858. $propal = new Propal($this->db);
  859. $result = $propal->fetch($proposalid);
  860. if (!$result) {
  861. throw new RestException(404, 'Proposal not found');
  862. }
  863. $result = $this->commande->createFromProposal($propal, DolibarrApiAccess::$user);
  864. if ($result < 0) {
  865. throw new RestException(405, $this->commande->error);
  866. }
  867. $this->commande->fetchObjectLinked();
  868. return $this->_cleanObjectDatas($this->commande);
  869. }
  870. /**
  871. * Get the shipments of an order
  872. *
  873. * @param int $id Id of the order
  874. *
  875. * @url GET {id}/shipment
  876. *
  877. * @return array
  878. *
  879. * @throws RestException 401
  880. * @throws RestException 404
  881. * @throws RestException 500 System error
  882. */
  883. public function getOrderShipments($id)
  884. {
  885. require_once DOL_DOCUMENT_ROOT.'/expedition/class/expedition.class.php';
  886. if (!DolibarrApiAccess::$user->rights->expedition->lire) {
  887. throw new RestException(401);
  888. }
  889. $obj_ret = array();
  890. $sql = "SELECT e.rowid";
  891. $sql .= " FROM ".MAIN_DB_PREFIX."expedition as e";
  892. $sql .= " JOIN ".MAIN_DB_PREFIX."expeditiondet as edet";
  893. $sql .= " ON e.rowid = edet.fk_expedition";
  894. $sql .= " JOIN ".MAIN_DB_PREFIX."commandedet as cdet";
  895. $sql .= " ON edet.fk_origin_line = cdet.rowid";
  896. $sql .= " JOIN ".MAIN_DB_PREFIX."commande as c";
  897. $sql .= " ON cdet.fk_commande = c.rowid";
  898. $sql .= " WHERE c.rowid = ".((int) $id);
  899. $sql .= " GROUP BY e.rowid";
  900. $sql .= $this->db->order("e.rowid", "ASC");
  901. dol_syslog("API Rest request");
  902. $result = $this->db->query($sql);
  903. if ($result) {
  904. $num = $this->db->num_rows($result);
  905. if ($num <= 0) {
  906. throw new RestException(404, 'Shipments not found ');
  907. }
  908. $i = 0;
  909. while ($i < $num) {
  910. $obj = $this->db->fetch_object($result);
  911. $shipment_static = new Expedition($this->db);
  912. if ($shipment_static->fetch($obj->rowid)) {
  913. $obj_ret[] = $this->_cleanObjectDatas($shipment_static);
  914. }
  915. $i++;
  916. }
  917. } else {
  918. throw new RestException(500, 'Error when retrieve shipment list : '.$this->db->lasterror());
  919. }
  920. return $obj_ret;
  921. }
  922. /**
  923. * Create the shipment of an order
  924. *
  925. * @param int $id Id of the order
  926. * @param int $warehouse_id Id of a warehouse
  927. *
  928. * @url POST {id}/shipment/{warehouse_id}
  929. *
  930. * @return int
  931. *
  932. * @throws RestException 401
  933. * @throws RestException 404
  934. * @throws RestException 500 System error
  935. */
  936. public function createOrderShipment($id, $warehouse_id)
  937. {
  938. require_once DOL_DOCUMENT_ROOT.'/expedition/class/expedition.class.php';
  939. if (!DolibarrApiAccess::$user->rights->expedition->creer) {
  940. throw new RestException(401);
  941. }
  942. if ($warehouse_id <= 0) {
  943. throw new RestException(404, 'Warehouse not found');
  944. }
  945. $result = $this->commande->fetch($id);
  946. if (!$result) {
  947. throw new RestException(404, 'Order not found');
  948. }
  949. $shipment = new Expedition($this->db);
  950. $shipment->socid = $this->commande->socid;
  951. $shipment->origin_id = $this->commande->id;
  952. $result = $shipment->create(DolibarrApiAccess::$user);
  953. if ($result <= 0) {
  954. throw new RestException(500, 'Error on creating expedition :'.$this->db->lasterror());
  955. }
  956. foreach ($this->commande->lines as $line) {
  957. $result = $shipment->create_line($warehouse_id, $line->id, $line->qty);
  958. if ($result <= 0) {
  959. throw new RestException(500, 'Error on creating expedition lines:'.$this->db->lasterror());
  960. }
  961. }
  962. return $shipment->id;
  963. }
  964. // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
  965. /**
  966. * Clean sensible object datas
  967. *
  968. * @param Object $object Object to clean
  969. * @return Object Object with cleaned properties
  970. */
  971. protected function _cleanObjectDatas($object)
  972. {
  973. // phpcs:enable
  974. $object = parent::_cleanObjectDatas($object);
  975. unset($object->note);
  976. unset($object->address);
  977. unset($object->barcode_type);
  978. unset($object->barcode_type_code);
  979. unset($object->barcode_type_label);
  980. unset($object->barcode_type_coder);
  981. return $object;
  982. }
  983. /**
  984. * Validate fields before create or update object
  985. *
  986. * @param array $data Array with data to verify
  987. * @return array
  988. * @throws RestException
  989. */
  990. private function _validate($data)
  991. {
  992. $commande = array();
  993. foreach (Orders::$FIELDS as $field) {
  994. if (!isset($data[$field])) {
  995. throw new RestException(400, $field." field missing");
  996. }
  997. $commande[$field] = $data[$field];
  998. }
  999. return $commande;
  1000. }
  1001. }