Merge remote-tracking branch 'upstream/4.3' into 4.3-master-2013-05-21-13-15-18
[civicrm-core.git] / api / v2 / Contribution.php
CommitLineData
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 */
45require_once 'api/v2/utils.php';
46require_once 'CRM/Utils/Rule.php';
47require_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 */
58function &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 */
94function 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 */
110function &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 */
148function 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 */
176function &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 */
242function &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 */
285function _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 */
343function _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 */
371function _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 */
497function 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