Commit | Line | Data |
---|---|---|
31a99426 PN |
1 | <?php |
2 | /* | |
3 | +--------------------------------------------------------------------+ | |
a30c801b | 4 | | Copyright CiviCRM LLC. All rights reserved. | |
31a99426 | 5 | | | |
a30c801b TO |
6 | | This work is published under the GNU AGPLv3 license with some | |
7 | | permitted exceptions and without any warranty. For full license | | |
8 | | and copyright information, see https://civicrm.org/licensing | | |
31a99426 PN |
9 | +--------------------------------------------------------------------+ |
10 | */ | |
11 | ||
12 | /** | |
7cad285e JM |
13 | * This api exposes CiviCRM Order objects, an abstract entity |
14 | * comprised of contributions and related line items. | |
31a99426 PN |
15 | * |
16 | * @package CiviCRM_APIv3 | |
17 | */ | |
18 | ||
19 | /** | |
20 | * Retrieve a set of Order. | |
21 | * | |
22 | * @param array $params | |
23 | * Input parameters. | |
24 | * | |
25 | * @return array | |
26 | * Array of Order, if error an array with an error id and error message | |
ee95478a | 27 | * @throws \CiviCRM_API3_Exception |
31a99426 | 28 | */ |
ee95478a | 29 | function civicrm_api3_order_get(array $params): array { |
cf8f0fff CW |
30 | $contributions = []; |
31 | $params['api.line_item.get'] = ['qty' => ['<>' => 0]]; | |
31a99426 | 32 | $isSequential = FALSE; |
de6c59ca | 33 | if (!empty($params['sequential'])) { |
31a99426 PN |
34 | $params['sequential'] = 0; |
35 | $isSequential = TRUE; | |
36 | } | |
37 | $result = civicrm_api3('Contribution', 'get', $params); | |
38 | if (!empty($result['values'])) { | |
39 | foreach ($result['values'] as $key => $contribution) { | |
40 | $contributions[$key] = $contribution; | |
41 | $contributions[$key]['line_items'] = $contribution['api.line_item.get']['values']; | |
42 | unset($contributions[$key]['api.line_item.get']); | |
43 | } | |
44 | } | |
45 | $params['sequential'] = $isSequential; | |
46 | return civicrm_api3_create_success($contributions, $params, 'Order', 'get'); | |
47 | } | |
b8644ae3 | 48 | |
785f03e2 | 49 | /** |
50 | * Adjust Metadata for Get action. | |
51 | * | |
52 | * The metadata is used for setting defaults, documentation & validation. | |
53 | * | |
54 | * @param array $params | |
55 | * Array of parameters determined by getfields. | |
56 | */ | |
ee95478a | 57 | function _civicrm_api3_order_get_spec(array &$params) { |
785f03e2 | 58 | $params['id']['api.aliases'] = ['order_id']; |
59 | $params['id']['title'] = ts('Contribution / Order ID'); | |
60 | } | |
61 | ||
b8644ae3 PN |
62 | /** |
63 | * Add or update a Order. | |
64 | * | |
65 | * @param array $params | |
66 | * Input parameters. | |
67 | * | |
b8644ae3 PN |
68 | * @return array |
69 | * Api result array | |
9c5edcd4 | 70 | * |
71 | * @throws \CiviCRM_API3_Exception | |
72 | * @throws API_Exception | |
b8644ae3 | 73 | */ |
ee95478a | 74 | function civicrm_api3_order_create(array $params): array { |
9c5edcd4 | 75 | civicrm_api3_verify_one_mandatory($params, NULL, ['line_items', 'total_amount']); |
ca1b238b EM |
76 | if (empty($params['skipCleanMoney'])) { |
77 | // We have to do this for v3 api - sadly. For v4 it will be no more. | |
78 | foreach (['total_amount', 'net_amount', 'fee_amount', 'non_deductible_amount'] as $field) { | |
79 | if (isset($params[$field])) { | |
80 | $params[$field] = CRM_Utils_Rule::cleanMoney($params[$field]); | |
81 | } | |
82 | } | |
83 | $params['skipCleanMoney'] = TRUE; | |
84 | } | |
5ad21509 | 85 | $params['contribution_status_id'] = 'Pending'; |
ca44bb7e EM |
86 | $order = new CRM_Financial_BAO_Order(); |
87 | $order->setDefaultFinancialTypeID($params['financial_type_id'] ?? NULL); | |
e2887a3c | 88 | |
de6c59ca | 89 | if (!empty($params['line_items']) && is_array($params['line_items'])) { |
ca44bb7e | 90 | foreach ($params['line_items'] as $index => $lineItems) { |
ca1b238b EM |
91 | if (!empty($lineItems['params'])) { |
92 | $order->setEntityParameters($lineItems['params'], $index); | |
93 | } | |
ca44bb7e EM |
94 | foreach ($lineItems['line_item'] as $innerIndex => $lineItem) { |
95 | $lineIndex = $index . '+' . $innerIndex; | |
96 | $order->setLineItem($lineItem, $lineIndex); | |
ca1b238b | 97 | $order->addLineItemToEntityParameters($lineIndex, $index); |
b8644ae3 | 98 | } |
b8644ae3 PN |
99 | } |
100 | } | |
ca44bb7e EM |
101 | else { |
102 | $order->setPriceSetToDefault('contribution'); | |
ca1b238b EM |
103 | $order->setLineItem([ |
104 | // Historically total_amount in this case could be tax | |
105 | // inclusive if tax is also supplied. | |
106 | // This is inconsistent with the contribution api.... | |
107 | 'line_total' => ((float) $params['total_amount'] - (float) ($params['tax_amount'] ?? 0)), | |
108 | 'financial_type_id' => (int) $params['financial_type_id'], | |
109 | ], 0); | |
ca44bb7e | 110 | } |
ca1b238b EM |
111 | // Only check the amount if line items are set because that is what we have historically |
112 | // done and total amount is historically only inclusive of tax_amount IF | |
113 | // tax amount is also passed in it seems | |
114 | if (isset($params['total_amount']) && !empty($params['line_items'])) { | |
115 | $currency = $params['currency'] ?? CRM_Core_Config::singleton()->defaultCurrency; | |
116 | if (!CRM_Utils_Money::equals($params['total_amount'], $order->getTotalAmount(), $currency)) { | |
117 | throw new CRM_Contribute_Exception_CheckLineItemsException(); | |
118 | } | |
119 | } | |
120 | $params['total_amount'] = $order->getTotalAmount(); | |
121 | ||
122 | foreach ($order->getEntitiesToCreate() as $entityParams) { | |
123 | if ($entityParams['entity'] === 'participant') { | |
124 | if (isset($entityParams['participant_status_id']) | |
125 | && (!CRM_Event_BAO_ParticipantStatusType::getIsValidStatusForClass($entityParams['participant_status_id'], 'Pending'))) { | |
126 | throw new CiviCRM_API3_Exception('Creating a participant via the Order API with a non "pending" status is not supported'); | |
127 | } | |
128 | $entityParams['participant_status_id'] = $entityParams['participant_status_id'] ?? 'Pending from incomplete transaction'; | |
129 | $entityParams['status_id'] = $entityParams['participant_status_id']; | |
130 | $entityParams['skipLineItem'] = TRUE; | |
131 | $entityResult = civicrm_api3('Participant', 'create', $entityParams); | |
132 | // @todo - once membership is cleaned up & financial validation tests are extended | |
133 | // we can look at removing this - some weird handling in removeFinancialAccounts | |
134 | $params['contribution_mode'] = 'participant'; | |
135 | $params['participant_id'] = $entityResult['id']; | |
136 | foreach ($entityParams['line_references'] as $lineIndex) { | |
137 | $order->setLineItemValue('entity_id', $entityResult['id'], $lineIndex); | |
138 | } | |
139 | } | |
140 | ||
141 | if ($entityParams['entity'] === 'membership') { | |
142 | $entityParams['status_id'] = 'Pending'; | |
143 | if (!empty($params['contribution_recur_id'])) { | |
144 | $entityParams['contribution_recur_id'] = $params['contribution_recur_id']; | |
145 | } | |
146 | $entityParams['skipLineItem'] = TRUE; | |
147 | $entityResult = civicrm_api3('Membership', 'create', $entityParams); | |
148 | foreach ($entityParams['line_references'] as $lineIndex) { | |
149 | $order->setLineItemValue('entity_id', $entityResult['id'], $lineIndex); | |
150 | } | |
151 | } | |
152 | } | |
153 | ||
154 | $params['line_item'][$order->getPriceSetID()] = $order->getLineItems(); | |
ca44bb7e | 155 | |
e2887a3c | 156 | $contributionParams = $params; |
0dea0c7c | 157 | // If this is nested we need to set sequential to 0 as sequential handling is done |
158 | // in create_success & id will be miscalculated... | |
159 | $contributionParams['sequential'] = 0; | |
e2887a3c | 160 | foreach ($contributionParams as $key => $value) { |
161 | // Unset chained keys so the code does not attempt to do this chaining twice. | |
162 | // e.g if calling 'api.Payment.create' We want to finish creating the order first. | |
163 | // it would probably be better to have a full whitelist of contributionParams | |
164 | if (substr($key, 0, 3) === 'api') { | |
165 | unset($contributionParams[$key]); | |
166 | } | |
167 | } | |
168 | ||
169 | $contribution = civicrm_api3('Contribution', 'create', $contributionParams); | |
26076348 | 170 | $contribution['values'][$contribution['id']]['line_item'] = array_values($order->getLineItems()); |
e376b300 | 171 | |
dffc9c5f | 172 | return civicrm_api3_create_success($contribution['values'] ?? [], $params, 'Order', 'create'); |
b8644ae3 PN |
173 | } |
174 | ||
5f44f698 PN |
175 | /** |
176 | * Delete a Order. | |
177 | * | |
178 | * @param array $params | |
179 | * Input parameters. | |
ee95478a | 180 | * |
5f44f698 | 181 | * @return array |
8089541a SL |
182 | * @throws API_Exception |
183 | * @throws CiviCRM_API3_Exception | |
5f44f698 | 184 | */ |
ee95478a | 185 | function civicrm_api3_order_delete(array $params): array { |
cf8f0fff CW |
186 | $contribution = civicrm_api3('Contribution', 'get', [ |
187 | 'return' => ['is_test'], | |
95c4e89e | 188 | 'id' => $params['id'], |
cf8f0fff | 189 | ]); |
5f44f698 PN |
190 | if ($contribution['id'] && $contribution['values'][$contribution['id']]['is_test'] == TRUE) { |
191 | $result = civicrm_api3('Contribution', 'delete', $params); | |
192 | } | |
193 | else { | |
95c4e89e | 194 | throw new API_Exception('Only test orders can be deleted.'); |
5f44f698 | 195 | } |
95c4e89e | 196 | return civicrm_api3_create_success($result['values'], $params, 'Order', 'delete'); |
5f44f698 PN |
197 | } |
198 | ||
65f7f9f6 | 199 | /** |
9d8e81c8 | 200 | * Cancel an Order. |
65f7f9f6 PN |
201 | * |
202 | * @param array $params | |
203 | * Input parameters. | |
204 | * | |
205 | * @return array | |
ee95478a | 206 | * @throws \CiviCRM_API3_Exception |
65f7f9f6 | 207 | */ |
ee95478a | 208 | function civicrm_api3_order_cancel(array $params) { |
65f7f9f6 | 209 | $contributionStatuses = CRM_Contribute_PseudoConstant::contributionStatus(NULL, 'name'); |
a5c7fd46 | 210 | $params['contribution_status_id'] = array_search('Cancelled', $contributionStatuses); |
65f7f9f6 | 211 | $result = civicrm_api3('Contribution', 'create', $params); |
9d8e81c8 | 212 | return civicrm_api3_create_success($result['values'], $params, 'Order', 'cancel'); |
65f7f9f6 PN |
213 | } |
214 | ||
215 | /** | |
216 | * Adjust Metadata for Cancel action. | |
217 | * | |
218 | * The metadata is used for setting defaults, documentation & validation. | |
219 | * | |
220 | * @param array $params | |
221 | * Array of parameters determined by getfields. | |
222 | */ | |
ee95478a | 223 | function _civicrm_api3_order_cancel_spec(array &$params) { |
cf8f0fff | 224 | $params['contribution_id'] = [ |
7c31ae57 | 225 | 'api.required' => 1, |
65f7f9f6 PN |
226 | 'title' => 'Contribution ID', |
227 | 'type' => CRM_Utils_Type::T_INT, | |
cf8f0fff | 228 | ]; |
65f7f9f6 PN |
229 | } |
230 | ||
b8644ae3 PN |
231 | /** |
232 | * Adjust Metadata for Create action. | |
233 | * | |
234 | * The metadata is used for setting defaults, documentation & validation. | |
235 | * | |
236 | * @param array $params | |
237 | * Array of parameters determined by getfields. | |
238 | */ | |
ee95478a | 239 | function _civicrm_api3_order_create_spec(array &$params) { |
cf8f0fff | 240 | $params['contact_id'] = [ |
b8644ae3 PN |
241 | 'name' => 'contact_id', |
242 | 'title' => 'Contact ID', | |
243 | 'type' => CRM_Utils_Type::T_INT, | |
244 | 'api.required' => TRUE, | |
cf8f0fff CW |
245 | ]; |
246 | $params['total_amount'] = [ | |
b8644ae3 PN |
247 | 'name' => 'total_amount', |
248 | 'title' => 'Total Amount', | |
cf8f0fff | 249 | ]; |
2c35902f | 250 | $params['skipCleanMoney'] = [ |
251 | 'api.default' => TRUE, | |
252 | 'title' => 'Do not attempt to convert money values', | |
253 | 'type' => CRM_Utils_Type::T_BOOLEAN, | |
254 | ]; | |
785f03e2 | 255 | $params['financial_type_id'] = [ |
b8644ae3 PN |
256 | 'name' => 'financial_type_id', |
257 | 'title' => 'Financial Type', | |
258 | 'type' => CRM_Utils_Type::T_INT, | |
259 | 'api.required' => TRUE, | |
785f03e2 | 260 | 'table_name' => 'civicrm_contribution', |
261 | 'entity' => 'Contribution', | |
262 | 'bao' => 'CRM_Contribute_BAO_Contribution', | |
263 | 'pseudoconstant' => [ | |
264 | 'table' => 'civicrm_financial_type', | |
265 | 'keyColumn' => 'id', | |
266 | 'labelColumn' => 'name', | |
267 | ], | |
268 | ]; | |
b8644ae3 | 269 | } |
5f44f698 PN |
270 | |
271 | /** | |
272 | * Adjust Metadata for Delete action. | |
273 | * | |
274 | * The metadata is used for setting defaults, documentation & validation. | |
275 | * | |
276 | * @param array $params | |
277 | * Array of parameters determined by getfields. | |
278 | */ | |
ee95478a | 279 | function _civicrm_api3_order_delete_spec(array &$params) { |
cf8f0fff | 280 | $params['contribution_id'] = [ |
5f44f698 PN |
281 | 'api.required' => TRUE, |
282 | 'title' => 'Contribution ID', | |
283 | 'type' => CRM_Utils_Type::T_INT, | |
cf8f0fff CW |
284 | ]; |
285 | $params['id']['api.aliases'] = ['contribution_id']; | |
5f44f698 | 286 | } |