Commit | Line | Data |
---|---|---|
6a488035 TO |
1 | <?php |
2 | // $Id: Contribution.php 45502 2013-02-08 13:32:55Z kurund $ | |
3 | ||
4 | ||
5 | /* | |
6 | +--------------------------------------------------------------------+ | |
7 | | CiviCRM version 4.3 | | |
8 | +--------------------------------------------------------------------+ | |
9 | | Copyright CiviCRM LLC (c) 2004-2013 | | |
10 | +--------------------------------------------------------------------+ | |
11 | | This file is a part of CiviCRM. | | |
12 | | | | |
13 | | CiviCRM is free software; you can copy, modify, and distribute it | | |
14 | | under the terms of the GNU Affero General Public License | | |
15 | | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. | | |
16 | | | | |
17 | | CiviCRM is distributed in the hope that it will be useful, but | | |
18 | | WITHOUT ANY WARRANTY; without even the implied warranty of | | |
19 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | | |
20 | | See the GNU Affero General Public License for more details. | | |
21 | | | | |
22 | | You should have received a copy of the GNU Affero General Public | | |
23 | | License and the CiviCRM Licensing Exception along | | |
24 | | with this program; if not, contact CiviCRM LLC | | |
25 | | at info[AT]civicrm[DOT]org. If you have questions about the | | |
26 | | GNU Affero General Public License or the licensing of CiviCRM, | | |
27 | | see the CiviCRM license FAQ at http://civicrm.org/licensing | | |
28 | +--------------------------------------------------------------------+ | |
29 | */ | |
30 | ||
31 | /** | |
32 | * File for the CiviCRM APIv2 Contribution functions | |
33 | * | |
34 | * @package CiviCRM_APIv2 | |
35 | * @subpackage API_Contribute | |
36 | * | |
37 | * @copyright CiviCRM LLC (c) 2004-2013 | |
38 | * @version $Id: Contribution.php 45502 2013-02-08 13:32:55Z kurund $ | |
39 | * | |
40 | */ | |
41 | ||
42 | /** | |
43 | * Include utility functions | |
44 | */ | |
45 | require_once 'api/v2/utils.php'; | |
46 | require_once 'CRM/Utils/Rule.php'; | |
47 | require_once 'CRM/Contribute/PseudoConstant.php'; | |
48 | ||
49 | /** | |
50 | * Add or update a contribution | |
51 | * | |
52 | * @param array $params (reference ) input parameters | |
53 | * | |
54 | * @return array (reference ) contribution_id of created or updated record | |
55 | * @static void | |
56 | * @access public | |
57 | */ | |
58 | function &civicrm_contribution_create(&$params) { | |
59 | _civicrm_initialize(); | |
60 | ||
61 | $error = _civicrm_contribute_check_params($params); | |
62 | if (civicrm_error($error)) { | |
63 | return $error; | |
64 | } | |
65 | ||
66 | $values = array(); | |
67 | ||
68 | require_once 'CRM/Contribute/BAO/Contribution.php'; | |
69 | $error = _civicrm_contribute_format_params($params, $values); | |
70 | if (civicrm_error($error)) { | |
71 | return $error; | |
72 | } | |
73 | ||
74 | $values['contact_id'] = $params['contact_id']; | |
75 | $values['source'] = CRM_Utils_Array::value('source', $params); | |
76 | $values['skipRecentView'] = TRUE; | |
77 | $ids = array(); | |
78 | if (CRM_Utils_Array::value('id', $params)) { | |
79 | $ids['contribution'] = $params['id']; | |
80 | } | |
81 | $contribution = CRM_Contribute_BAO_Contribution::create($values, $ids); | |
82 | if (is_a($contribution, 'CRM_Core_Error')) { | |
83 | return civicrm_create_error(ts($contribution->_errors[0]['message'])); | |
84 | } | |
85 | ||
86 | _civicrm_object_to_array($contribution, $contributeArray); | |
87 | ||
88 | return $contributeArray; | |
89 | } | |
90 | /* | |
91 | * Deprecated wrapper function | |
92 | * @deprecated | |
93 | */ | |
94 | function civicrm_contribution_add(&$params) { | |
95 | $result = civicrm_contribution_create($params); | |
96 | return $result; | |
97 | } | |
98 | ||
99 | /** | |
100 | * Retrieve a specific contribution, given a set of input params | |
101 | * If more than one contribution exists, return an error, unless | |
102 | * the client has requested to return the first found contact | |
103 | * | |
104 | * @param array $params (reference ) input parameters | |
105 | * | |
106 | * @return array (reference ) array of properties, if error an array with an error id and error message | |
107 | * @static void | |
108 | * @access public | |
109 | */ | |
110 | function &civicrm_contribution_get(&$params) { | |
111 | _civicrm_initialize(); | |
112 | ||
113 | $values = array(); | |
114 | if (empty($params)) { | |
115 | return civicrm_create_error(ts('No input parameters present')); | |
116 | } | |
117 | ||
118 | if (!is_array($params)) { | |
119 | return civicrm_create_error(ts('Input parameters is not an array')); | |
120 | } | |
121 | ||
122 | $contributions = &civicrm_contribution_search($params); | |
123 | if (civicrm_error($contributions)) { | |
124 | return $contributions; | |
125 | } | |
126 | ||
127 | if (count($contributions) != 1 && | |
128 | !$params['returnFirst'] | |
129 | ) { | |
130 | return civicrm_create_error(ts('%1 contributions matching input params', array(1 => count($contributions))), | |
131 | $contributions | |
132 | ); | |
133 | } | |
134 | ||
135 | $contributions = array_values($contributions); | |
136 | return $contributions[0]; | |
137 | } | |
138 | ||
139 | /** | |
140 | * Delete a contribution | |
141 | * | |
142 | * @param array $params (reference ) input parameters | |
143 | * | |
144 | * @return boolean true if success, else false | |
145 | * @static void | |
146 | * @access public | |
147 | */ | |
148 | function civicrm_contribution_delete(&$params) { | |
149 | _civicrm_initialize(); | |
150 | $contributionID = CRM_Utils_Array::value('contribution_id', $params); | |
151 | if (!$contributionID) { | |
152 | return civicrm_create_error(ts('Could not find contribution_id in input parameters')); | |
153 | } | |
154 | ||
155 | require_once 'CRM/Contribute/BAO/Contribution.php'; | |
156 | if (CRM_Contribute_BAO_Contribution::deleteContribution($contributionID)) { | |
157 | return civicrm_create_success(); | |
158 | } | |
159 | else { | |
160 | return civicrm_create_error(ts('Could not delete contribution')); | |
161 | } | |
162 | } | |
163 | ||
164 | /** | |
165 | * Retrieve a set of contributions, given a set of input params | |
166 | * | |
167 | * @param array $params (reference ) input parameters | |
168 | * @param array $returnProperties Which properties should be included in the | |
169 | * returned Contribution object. If NULL, the default | |
170 | * set of properties will be included. | |
171 | * | |
172 | * @return array (reference ) array of contributions, if error an array with an error id and error message | |
173 | * @static void | |
174 | * @access public | |
175 | */ | |
176 | function &civicrm_contribution_search(&$params) { | |
177 | _civicrm_initialize(); | |
178 | ||
179 | if (!is_array($params)) { | |
180 | return civicrm_create_error(ts('Input parameters is not an array')); | |
181 | } | |
182 | ||
183 | $inputParams = array(); | |
184 | $returnProperties = array(); | |
185 | $otherVars = array('sort', 'offset', 'rowCount'); | |
186 | ||
187 | $sort = NULL; | |
188 | $offset = 0; | |
189 | $rowCount = 25; | |
190 | foreach ($params as $n => $v) { | |
191 | if (substr($n, 0, 7) == 'return.') { | |
192 | $returnProperties[substr($n, 7)] = $v; | |
193 | } | |
194 | elseif (in_array($n, $otherVars)) { | |
195 | $$n = $v; | |
196 | } | |
197 | else { | |
198 | $inputParams[$n] = $v; | |
199 | } | |
200 | } | |
201 | ||
202 | // add is_test to the clause if not present | |
203 | if (!array_key_exists('contribution_test', $inputParams)) { | |
204 | $inputParams['contribution_test'] = 0; | |
205 | } | |
206 | ||
207 | require_once 'CRM/Contribute/BAO/Query.php'; | |
208 | require_once 'CRM/Contact/BAO/Query.php'; | |
209 | if (empty($returnProperties)) { | |
210 | $returnProperties = CRM_Contribute_BAO_Query::defaultReturnProperties(CRM_Contact_BAO_Query::MODE_CONTRIBUTE); | |
211 | } | |
212 | ||
213 | $newParams = CRM_Contact_BAO_Query::convertFormValues($inputParams); | |
214 | ||
215 | $query = new CRM_Contact_BAO_Query($newParams, $returnProperties, NULL); | |
216 | list($select, $from, $where, $having) = $query->query(); | |
217 | ||
218 | $sql = "$select $from $where $having"; | |
219 | ||
220 | if (!empty($sort)) { | |
221 | $sql .= " ORDER BY $sort "; | |
222 | } | |
223 | $sql .= " LIMIT $offset, $rowCount "; | |
224 | $dao = CRM_Core_DAO::executeQuery($sql); | |
225 | ||
226 | $contribution = array(); | |
227 | while ($dao->fetch()) { | |
228 | $contribution[$dao->contribution_id] = $query->store($dao); | |
229 | } | |
230 | $dao->free(); | |
231 | ||
232 | return $contribution; | |
233 | } | |
234 | ||
235 | /** | |
236 | * | |
237 | * @param <type> $params | |
238 | * | |
239 | * @return <type> | |
240 | * @deprecated | |
241 | */ | |
242 | function &civicrm_contribution_format_create(&$params) { | |
243 | _civicrm_initialize(); | |
244 | ||
245 | // return error if we have no params | |
246 | if (empty($params)) { | |
247 | return civicrm_create_error('Input Parameters empty'); | |
248 | } | |
249 | ||
250 | $error = _civicrm_contribute_check_params($params); | |
251 | if (civicrm_error($error)) { | |
252 | return $error; | |
253 | } | |
254 | $values = array(); | |
255 | $error = _civicrm_contribute_format_params($params, $values); | |
256 | if (civicrm_error($error)) { | |
257 | return $error; | |
258 | } | |
259 | ||
260 | $error = _civicrm_contribute_duplicate_check($params); | |
261 | if (civicrm_error($error)) { | |
262 | return $error; | |
263 | } | |
264 | $ids = array(); | |
265 | ||
266 | CRM_Contribute_BAO_Contribution::resolveDefaults($params, TRUE); | |
267 | ||
268 | $contribution = CRM_Contribute_BAO_Contribution::create($params, $ids); | |
269 | _civicrm_object_to_array($contribution, $contributeArray); | |
270 | return $contributeArray; | |
271 | } | |
272 | ||
273 | /** | |
274 | * This function ensures that we have the right input contribution parameters | |
275 | * | |
276 | * We also need to make sure we run all the form rules on the params list | |
277 | * to ensure that the params are valid | |
278 | * | |
279 | * @param array $params Associative array of property name/value | |
280 | * pairs to insert in new contribution. | |
281 | * | |
282 | * @return bool|CRM_Utils_Error | |
283 | * @access private | |
284 | */ | |
285 | function _civicrm_contribute_check_params(&$params) { | |
286 | static $required = array('contact_id' => NULL, | |
287 | 'total_amount' => NULL, | |
288 | 'financial_type_id' => 'financial_type', | |
289 | ); | |
290 | ||
291 | // params should be an array | |
292 | if (!is_array($params)) { | |
293 | return civicrm_create_error(ts('Input parameters is not an array')); | |
294 | } | |
295 | ||
296 | // cannot create a contribution with empty params | |
297 | if (empty($params)) { | |
298 | return civicrm_create_error('Input Parameters empty'); | |
299 | } | |
300 | ||
301 | $valid = TRUE; | |
302 | $error = ''; | |
303 | ||
304 | // check params for contribution id during update | |
305 | if (CRM_Utils_Array::value('id', $params)) { | |
306 | require_once 'CRM/Contribute/BAO/Contribution.php'; | |
307 | $contributor = new CRM_Contribute_BAO_Contribution(); | |
308 | $contributor->id = $params['id']; | |
309 | if (!$contributor->find(TRUE)) { | |
310 | return civicrm_create_error(ts('Contribution id is not valid')); | |
311 | } | |
312 | // do not check other field during update | |
313 | return array(); | |
314 | } | |
315 | ||
316 | foreach ($required as $field => $eitherField) { | |
317 | if (!CRM_Utils_Array::value($field, $params)) { | |
318 | if ($eitherField && CRM_Utils_Array::value($eitherField, $params)) { | |
319 | continue; | |
320 | } | |
321 | $valid = FALSE; | |
322 | $error .= $field; | |
323 | break; | |
324 | } | |
325 | } | |
326 | ||
327 | if (!$valid) { | |
328 | return civicrm_create_error("Required fields not found for contribution $error"); | |
329 | } | |
330 | ||
331 | return array(); | |
332 | } | |
333 | ||
334 | /** | |
335 | * Check if there is a contribution with the same trxn_id or invoice_id | |
336 | * | |
337 | * @param array $params Associative array of property name/value | |
338 | * pairs to insert in new contribution. | |
339 | * | |
340 | * @return array|CRM_Error | |
341 | * @access private | |
342 | */ | |
343 | function _civicrm_contribute_duplicate_check(&$params) { | |
344 | require_once 'CRM/Contribute/BAO/Contribution.php'; | |
345 | $duplicates = array(); | |
346 | $result = CRM_Contribute_BAO_Contribution::checkDuplicate($params, $duplicates); | |
347 | if ($result) { | |
348 | $d = implode(', ', $duplicates); | |
349 | $error = CRM_Core_Error::createError("Duplicate error - existing contribution record(s) have a matching Transaction ID or Invoice ID. Contribution record ID(s) are: $d", CRM_Core_Error::DUPLICATE_CONTRIBUTION, 'Fatal', $d); | |
350 | return civicrm_create_error($error->pop(), | |
351 | $d | |
352 | ); | |
353 | } | |
354 | else { | |
355 | return array(); | |
356 | } | |
357 | } | |
358 | ||
359 | /** | |
360 | * take the input parameter list as specified in the data model and | |
361 | * convert it into the same format that we use in QF and BAO object | |
362 | * | |
363 | * @param array $params Associative array of property name/value | |
364 | * pairs to insert in new contact. | |
365 | * @param array $values The reformatted properties that we can use internally | |
366 | * ' | |
367 | * | |
368 | * @return array|CRM_Error | |
369 | * @access public | |
370 | */ | |
371 | function _civicrm_contribute_format_params(&$params, &$values, $create = FALSE) { | |
372 | // copy all the contribution fields as is | |
373 | ||
374 | $fields = CRM_Contribute_DAO_Contribution::fields(); | |
375 | ||
376 | _civicrm_store_values($fields, $params, $values); | |
377 | ||
378 | foreach ($params as $key => $value) { | |
379 | // ignore empty values or empty arrays etc | |
380 | if (CRM_Utils_System::isNull($value)) { | |
381 | continue; | |
382 | } | |
383 | ||
384 | switch ($key) { | |
385 | case 'contribution_contact_id': | |
386 | if (!CRM_Utils_Rule::integer($value)) { | |
387 | return civicrm_create_error("contact_id not valid: $value"); | |
388 | } | |
389 | $dao = new CRM_Core_DAO(); | |
390 | $qParams = array(); | |
391 | $svq = $dao->singleValueQuery("SELECT id FROM civicrm_contact WHERE id = $value", | |
392 | $qParams | |
393 | ); | |
394 | if (!$svq) { | |
395 | return civicrm_create_error("Invalid Contact ID: There is no contact record with contact_id = $value."); | |
396 | } | |
397 | ||
398 | $values['contact_id'] = $values['contribution_contact_id']; | |
399 | unset($values['contribution_contact_id']); | |
400 | break; | |
401 | ||
402 | case 'receive_date': | |
403 | case 'cancel_date': | |
404 | case 'receipt_date': | |
405 | case 'thankyou_date': | |
406 | if (!CRM_Utils_Rule::dateTime($value)) { | |
407 | return civicrm_create_error("$key not a valid date: $value"); | |
408 | } | |
409 | break; | |
410 | ||
411 | case 'non_deductible_amount': | |
412 | case 'total_amount': | |
413 | case 'fee_amount': | |
414 | case 'net_amount': | |
415 | if (!CRM_Utils_Rule::money($value)) { | |
416 | return civicrm_create_error("$key not a valid amount: $value"); | |
417 | } | |
418 | break; | |
419 | ||
420 | case 'currency': | |
421 | if (!CRM_Utils_Rule::currencyCode($value)) { | |
422 | return civicrm_create_error("currency not a valid code: $value"); | |
423 | } | |
424 | break; | |
425 | case 'financial_type_id' : | |
426 | if (!CRM_Utils_Array::value($value, CRM_Contribute_PseudoConstant::financialType())) { | |
427 | return civicrm_create_error('Invalid Financial Type Id'); | |
428 | } | |
429 | break; | |
430 | ||
431 | case 'financial_type': | |
432 | $contributionTypeId = CRM_Utils_Array::key(ucfirst($value), | |
433 | CRM_Contribute_PseudoConstant::financialType() | |
434 | ); | |
435 | if ($contributionTypeId) { | |
436 | if (CRM_Utils_Array::value('financial_type_id', $values) && | |
437 | $contributionTypeId != $values['financial_type_id'] | |
438 | ) { | |
439 | return civicrm_create_error('Mismatched Financial Type and Financial Type Id'); | |
440 | } | |
441 | $values['financial_type_id'] = $contributionTypeId; | |
442 | } | |
443 | else { | |
444 | return civicrm_create_error('Invalid Financial Type'); | |
445 | } | |
446 | break; | |
447 | ||
448 | case 'payment_instrument': | |
449 | require_once 'CRM/Core/OptionGroup.php'; | |
450 | $values['payment_instrument_id'] = CRM_Core_OptionGroup::getValue('payment_instrument', $value); | |
451 | break; | |
452 | ||
453 | case 'soft_credit_to': | |
454 | if (!CRM_Utils_Rule::integer($value)) { | |
455 | return civicrm_create_error("$key not a valid Id: $value"); | |
456 | } | |
457 | $values['soft_credit_to'] = $value; | |
458 | break; | |
459 | ||
460 | default: | |
461 | break; | |
462 | } | |
463 | } | |
464 | ||
465 | if (array_key_exists('note', $params)) { | |
466 | $values['note'] = $params['note']; | |
467 | } | |
468 | ||
469 | _civicrm_custom_format_params($params, $values, 'Contribution'); | |
470 | ||
471 | if ($create) { | |
472 | // CRM_Contribute_BAO_Contribution::add() handles contribution_source | |
473 | // So, if $values contains contribution_source, convert it to source | |
474 | $changes = array('contribution_source' => 'source'); | |
475 | ||
476 | foreach ($changes as $orgVal => $changeVal) { | |
477 | if (isset($values[$orgVal])) { | |
478 | $values[$changeVal] = $values[$orgVal]; | |
479 | unset($values[$orgVal]); | |
480 | } | |
481 | } | |
482 | } | |
483 | ||
484 | return array(); | |
485 | } | |
486 | ||
487 | /** | |
488 | * Process a transaction and record it against the contact. | |
489 | * | |
490 | * @param array $params (reference ) input parameters | |
491 | * | |
492 | * @return array (reference ) contribution of created or updated record (or a civicrm error) | |
493 | * @static void | |
494 | * @access public | |
495 | * | |
496 | */ | |
497 | function civicrm_contribute_transact($params) { | |
498 | civicrm_initialize(); | |
499 | ||
500 | if (empty($params)) { | |
501 | return civicrm_create_error(ts('No input parameters present')); | |
502 | } | |
503 | ||
504 | if (!is_array($params)) { | |
505 | return civicrm_create_error(ts('Input parameters is not an array')); | |
506 | } | |
507 | ||
508 | $values = array(); | |
509 | ||
510 | require_once 'CRM/Contribute/BAO/Contribution.php'; | |
511 | $error = _civicrm_contribute_format_params($params, $values); | |
512 | if (civicrm_error($error)) { | |
513 | return $error; | |
514 | } | |
515 | ||
516 | $required = array( | |
517 | 'amount', | |
518 | ); | |
519 | foreach ($required as $key) { | |
520 | if (!isset($params[$key])) { | |
521 | return civicrm_create_error("Missing parameter $key: civicrm_contribute_transact() requires a parameter '$key'."); | |
522 | } | |
523 | } | |
524 | ||
525 | // allow people to omit some values for convenience | |
526 | $defaults = array( | |
527 | // 'payment_processor_id' => NULL /* we could retrieve the default processor here, but only if it's missing to avoid an extra lookup */ | |
528 | 'payment_processor_mode' => 'live', | |
529 | ); | |
530 | $params = array_merge($defaults, $params); | |
531 | ||
532 | // clean up / adjust some values which | |
533 | if (!isset($params['total_amount'])) { | |
534 | $params['total_amount'] = $params['amount']; | |
535 | } | |
536 | if (!isset($params['net_amount'])) { | |
537 | $params['net_amount'] = $params['amount']; | |
538 | } | |
539 | if (!isset($params['receive_date'])) { | |
540 | $params['receive_date'] = date('Y-m-d'); | |
541 | } | |
542 | if (!isset($params['invoiceID']) && isset($params['invoice_id'])) { | |
543 | $params['invoiceID'] = $params['invoice_id']; | |
544 | } | |
545 | ||
546 | require_once 'CRM/Financial/BAO/PaymentProcessor.php'; | |
547 | $paymentProcessor = CRM_Financial_BAO_PaymentProcessor::getPayment($params['payment_processor_id'], | |
548 | $params['payment_processor_mode'] | |
549 | ); | |
550 | if (civicrm_error($paymentProcessor)) { | |
551 | return $paymentProcessor; | |
552 | } | |
553 | ||
554 | require_once 'CRM/Core/Payment.php'; | |
555 | $payment = CRM_Core_Payment::singleton($params['payment_processor_mode'], $paymentProcessor); | |
556 | if (civicrm_error($payment)) { | |
557 | return $payment; | |
558 | } | |
559 | ||
560 | $transaction = $payment->doDirectPayment($params); | |
561 | if (civicrm_error($transaction)) { | |
562 | return $transaction; | |
563 | } | |
564 | ||
565 | // but actually, $payment->doDirectPayment() doesn't return a | |
566 | // CRM_Core_Error by itself | |
567 | if (get_class($transaction) == 'CRM_Core_Error') { | |
568 | $errs = $transaction->getErrors(); | |
569 | if (!empty($errs)) { | |
570 | $last_error = array_shift($errs); | |
571 | return CRM_Core_Error::createApiError($last_error['message']); | |
572 | } | |
573 | } | |
574 | ||
575 | $contribution = civicrm_contribution_add($params); | |
576 | return $contribution; | |
577 | } | |
578 |