Merge pull request #9438 from jmcclelland/CRM-19298
[civicrm-core.git] / CRM / Contribute / BAO / Contribution / Utils.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
7e9e8871 4 | CiviCRM version 4.7 |
6a488035 5 +--------------------------------------------------------------------+
fa938177 6 | Copyright CiviCRM LLC (c) 2004-2016 |
6a488035
TO
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
d25dd0ee 26 */
6a488035
TO
27
28/**
29 *
30 * @package CRM
fa938177 31 * @copyright CiviCRM LLC (c) 2004-2016
6a488035
TO
32 */
33class CRM_Contribute_BAO_Contribution_Utils {
34
35 /**
fe482240 36 * Process payment after confirmation.
6a488035 37 *
014c4014
TO
38 * @param CRM_Core_Form $form
39 * Form object.
40 * @param array $paymentParams
41 * Array with payment related key.
16b10e64 42 * value pairs
014c4014
TO
43 * @param int $contactID
44 * Contact id.
45 * @param int $contributionTypeId
46 * Financial type id.
b8950542 47 * @param int|string $component component id
449f4c90 48 * @param bool $isTest
49 * @param bool $isRecur
cc789d46
EM
50 *
51 * @throws CRM_Core_Exception
52 * @throws Exception
a6c01b45
CW
53 * @return array
54 * associated array
6a488035 55 *
6a488035 56 */
ae5ffbb7 57 public static function processConfirm(
a13f3d8c 58 &$form,
6a488035 59 &$paymentParams,
6a488035
TO
60 $contactID,
61 $contributionTypeId,
62 $component = 'contribution',
449f4c90 63 $isTest,
64 $isRecur
6a488035
TO
65 ) {
66 CRM_Core_Payment_Form::mapParams($form->_bltID, $form->_params, $paymentParams, TRUE);
cc789d46 67 $isPaymentTransaction = self::isPaymentTransaction($form);
43b309d0 68
68642f9a
EM
69 $financialType = new CRM_Financial_DAO_FinancialType();
70 $financialType->id = $contributionTypeId;
71 $financialType->find(TRUE);
72 if ($financialType->is_deductible) {
73 $form->assign('is_deductible', TRUE);
74 $form->set('is_deductible', TRUE);
6a488035
TO
75 }
76
77 // add some financial type details to the params list
78 // if folks need to use it
d9659858 79 //CRM-15297 - contributionType is obsolete - pass financial type as well so people can deprecate it
68642f9a 80 $paymentParams['financialType_name'] = $paymentParams['contributionType_name'] = $form->_params['contributionType_name'] = $financialType->name;
6a488035 81 //CRM-11456
874c9be7 82 $paymentParams['financialType_accounting_code'] = $paymentParams['contributionType_accounting_code'] = $form->_params['contributionType_accounting_code'] = CRM_Financial_BAO_FinancialAccount::getAccountingCode($contributionTypeId);
6a488035 83 $paymentParams['contributionPageID'] = $form->_params['contributionPageID'] = $form->_values['id'];
5fb28746 84 $paymentParams['contactID'] = $form->_params['contactID'] = $contactID;
6a488035 85
f1fc3638 86 //fix for CRM-16317
33dd32c8
E
87 if (empty($form->_params['receive_date'])) {
88 $form->_params['receive_date'] = date('YmdHis');
89 }
90 if (!empty($form->_params['start_date'])) {
91 $form->_params['start_date'] = date('YmdHis');
92 }
f1fc3638 93 $form->assign('receive_date',
94 CRM_Utils_Date::mysqlToIso($form->_params['receive_date'])
95 );
f6261e9d 96
0335c5f1 97 if (empty($form->_values['amount'])) {
98 // If the amount is not in _values[], set it
99 $form->_values['amount'] = $form->_params['amount'];
100 }
101
bd53b1b9 102 if ($isPaymentTransaction) {
3febe800 103 $contributionParams = array(
58466af1 104 'id' => CRM_Utils_Array::value('contribution_id', $paymentParams),
3febe800 105 'contact_id' => $contactID,
3febe800 106 'is_test' => $isTest,
107 'campaign_id' => CRM_Utils_Array::value('campaign_id', $paymentParams, CRM_Utils_Array::value('campaign_id', $form->_values)),
108 'contribution_page_id' => $form->_id,
109 'source' => CRM_Utils_Array::value('source', $paymentParams, CRM_Utils_Array::value('description', $paymentParams)),
110 );
8e8d287f 111 if (isset($paymentParams['line_item'])) {
112 // @todo make sure this is consisently set at this point.
113 $contributionParams['line_item'] = $paymentParams['line_item'];
114 }
f69a9ac3 115 if (!empty($form->_paymentProcessor)) {
116 $contributionParams['payment_instrument_id'] = $paymentParams['payment_instrument_id'] = $form->_paymentProcessor['payment_instrument_id'];
3febe800 117 }
f69a9ac3 118
ba013eea 119 $contribution = CRM_Contribute_Form_Contribution_Confirm::processFormContribution(
6a488035
TO
120 $form,
121 $paymentParams,
122 NULL,
3febe800 123 $contributionParams,
68642f9a 124 $financialType,
5fb28746 125 TRUE,
449f4c90 126 $form->_bltID,
127 $isRecur
6a488035
TO
128 );
129
5fb28746
EM
130 $paymentParams['contributionTypeID'] = $contributionTypeId;
131 $paymentParams['item_name'] = $form->_params['description'];
6a488035 132
5fb28746
EM
133 $paymentParams['qfKey'] = $form->controller->_key;
134 if ($component == 'membership') {
135 return array('contribution' => $contribution);
6a488035 136 }
06d062ce 137
5fb28746
EM
138 $paymentParams['contributionID'] = $contribution->id;
139 //CRM-15297 deprecate contributionTypeID
140 $paymentParams['financialTypeID'] = $paymentParams['contributionTypeID'] = $contribution->financial_type_id;
141 $paymentParams['contributionPageID'] = $contribution->contribution_page_id;
142 if (isset($paymentParams['contribution_source'])) {
143 $paymentParams['source'] = $paymentParams['contribution_source'];
6a488035 144 }
6a488035 145
7e3c24b9 146 if (CRM_Utils_Array::value('is_recur', $form->_params) && $contribution->contribution_recur_id) {
5fb28746 147 $paymentParams['contributionRecurID'] = $contribution->contribution_recur_id;
6a488035 148 }
cd3bc162 149 if (isset($paymentParams['contribution_source'])) {
150 $form->_params['source'] = $paymentParams['contribution_source'];
151 }
152
153 // get the price set values for receipt.
154 if ($form->_priceSetId && $form->_lineItem) {
155 $form->_values['lineItem'] = $form->_lineItem;
156 $form->_values['priceSetID'] = $form->_priceSetId;
157 }
158
159 $form->_values['contribution_id'] = $contribution->id;
160 $form->_values['contribution_page_id'] = $contribution->contribution_page_id;
6a488035 161
510fa17f 162 if (!empty($form->_paymentProcessor)) {
5fb28746
EM
163 try {
164 $payment = Civi\Payment\System::singleton()->getByProcessor($form->_paymentProcessor);
165 if ($form->_contributeMode == 'notify') {
166 // We want to get rid of this & make it generic - eg. by making payment processing the last thing
167 // and always calling it first.
168 $form->postProcessHook();
169 }
170 $result = $payment->doPayment($paymentParams);
171 $form->_params = array_merge($form->_params, $result);
172 $form->assign('trxn_id', CRM_Utils_Array::value('trxn_id', $result));
173 if (!empty($result['trxn_id'])) {
174 $contribution->trxn_id = $result['trxn_id'];
175 }
176 if (!empty($result['payment_status_id'])) {
177 $contribution->payment_status_id = $result['payment_status_id'];
178 }
179 $result['contribution'] = $contribution;
e05d2e11 180 if ($result['payment_status_id'] == CRM_Core_PseudoConstant::getKey('CRM_Contribute_BAO_Contribution',
cd3bc162 181 'status_id', 'Pending') && $payment->isSendReceiptForPending()) {
182 CRM_Contribute_BAO_ContributionPage::sendMail($contactID,
183 $form->_values,
184 $contribution->is_test
185 );
186 }
5fb28746 187 return $result;
68e61ad6 188 }
5fb28746
EM
189 catch (\Civi\Payment\Exception\PaymentProcessorException $e) {
190 // Clean up DB as appropriate.
191 if (!empty($paymentParams['contributionID'])) {
192 CRM_Contribute_BAO_Contribution::failPayment($paymentParams['contributionID'],
193 $paymentParams['contactID'], $e->getMessage());
194 }
195 if (!empty($paymentParams['contributionRecurID'])) {
196 CRM_Contribute_BAO_ContributionRecur::deleteRecurContribution($paymentParams['contributionRecurID']);
197 }
198
199 $result['is_payment_failure'] = TRUE;
200 $result['error'] = $e;
201 return $result;
68e61ad6 202 }
a7a33651 203 }
6a488035 204 }
222a33ae 205
cd3bc162 206 // Only pay later or unpaid should reach this point, although pay later likely does not & is handled via the
207 // manual processor, so it's unclear what this set is for and whether the following send ever fires.
5fb28746 208 $form->set('params', $form->_params);
6a488035 209
510fa17f 210 if ($form->_params['amount'] == 0) {
211 // This is kind of a back-up for pay-later $0 transactions.
212 // In other flows they pick up the manual processor & get dealt with above (I
213 // think that might be better...).
214 return array(
215 'payment_status_id' => 1,
216 'contribution' => $contribution,
217 'payment_processor_id' => 0,
218 );
219 }
0335c5f1 220
5fb28746
EM
221 CRM_Contribute_BAO_ContributionPage::sendMail($contactID,
222 $form->_values,
223 $contribution->is_test
224 );
6a488035
TO
225 }
226
cc789d46
EM
227 /**
228 * Is a payment being made.
229 * Note that setting is_monetary on the form is somewhat legacy and the behaviour around this setting is confusing. It would be preferable
230 * to look for the amount only (assuming this cannot refer to payment in goats or other non-monetary currency
c490a46a 231 * @param CRM_Core_Form $form
cc789d46
EM
232 *
233 * @return bool
234 */
235 static protected function isPaymentTransaction($form) {
bd53b1b9 236 return ($form->_amount >= 0.0) ? TRUE : FALSE;
cc789d46 237 }
353ffa53 238
6a488035 239 /**
3c536a11 240 * Get the contribution details by month of the year.
6a488035 241 *
014c4014
TO
242 * @param int $param
243 * Year.
6a488035 244 *
a6c01b45
CW
245 * @return array
246 * associated array
6a488035 247 */
00be9182 248 public static function contributionChartMonthly($param) {
6a488035
TO
249 if ($param) {
250 $param = array(1 => array($param, 'Integer'));
251 }
252 else {
253 $param = date("Y");
254 $param = array(1 => array($param, 'Integer'));
255 }
256
257 $query = "
258 SELECT sum(contrib.total_amount) AS ctAmt,
259 MONTH( contrib.receive_date) AS contribMonth
260 FROM civicrm_contribution AS contrib
261INNER JOIN civicrm_contact AS contact ON ( contact.id = contrib.contact_id )
262 WHERE contrib.contact_id = contact.id
263 AND ( contrib.is_test = 0 OR contrib.is_test IS NULL )
264 AND contrib.contribution_status_id = 1
265 AND date_format(contrib.receive_date,'%Y') = %1
266 AND contact.is_deleted = 0
267 GROUP BY contribMonth
268 ORDER BY month(contrib.receive_date)";
269
270 $dao = CRM_Core_DAO::executeQuery($query, $param);
271
272 $params = NULL;
273 while ($dao->fetch()) {
274 if ($dao->contribMonth) {
275 $params['By Month'][$dao->contribMonth] = $dao->ctAmt;
276 }
277 }
278 return $params;
279 }
280
281 /**
fe482240 282 * Get the contribution details by year.
6a488035 283 *
a6c01b45
CW
284 * @return array
285 * associated array
6a488035 286 */
00be9182 287 public static function contributionChartYearly() {
6a488035
TO
288 $config = CRM_Core_Config::singleton();
289 $yearClause = "year(contrib.receive_date) as contribYear";
290 if (!empty($config->fiscalYearStart) && ($config->fiscalYearStart['M'] != 1 || $config->fiscalYearStart['d'] != 1)) {
a48278eb
KJ
291 $yearClause = "CASE
292 WHEN (MONTH(contrib.receive_date)>= " . $config->fiscalYearStart['M'] . "
f813f78e 293 && DAYOFMONTH(contrib.receive_date)>= " . $config->fiscalYearStart['d'] . " )
a48278eb
KJ
294 THEN
295 concat(YEAR(contrib.receive_date), '-',YEAR(contrib.receive_date)+1)
296 ELSE
297 concat(YEAR(contrib.receive_date)-1,'-', YEAR(contrib.receive_date))
298 END AS contribYear";
6a488035
TO
299 }
300
301 $query = "
302 SELECT sum(contrib.total_amount) AS ctAmt,
303 {$yearClause}
304 FROM civicrm_contribution AS contrib
305INNER JOIN civicrm_contact contact ON ( contact.id = contrib.contact_id )
306 WHERE ( contrib.is_test = 0 OR contrib.is_test IS NULL )
307 AND contrib.contribution_status_id = 1
308 AND contact.is_deleted = 0
309 GROUP BY contribYear
310 ORDER BY contribYear";
311 $dao = CRM_Core_DAO::executeQuery($query);
312
313 $params = NULL;
314 while ($dao->fetch()) {
315 if (!empty($dao->contribYear)) {
316 $params['By Year'][$dao->contribYear] = $dao->ctAmt;
317 }
318 }
319 return $params;
320 }
321
186c9c17 322 /**
c490a46a 323 * @param array $params
100fef9d 324 * @param int $contactID
186c9c17
EM
325 * @param $mail
326 */
00be9182 327 public static function createCMSUser(&$params, $contactID, $mail) {
6a488035
TO
328 // lets ensure we only create one CMS user
329 static $created = FALSE;
330
331 if ($created) {
332 return;
333 }
334 $created = TRUE;
335
a7488080 336 if (!empty($params['cms_create_account'])) {
0462a5b2 337 $params['contactID'] = !empty($params['onbehalf_contact_id']) ? $params['onbehalf_contact_id'] : $contactID;
6a488035
TO
338 if (!CRM_Core_BAO_CMSUser::create($params, $mail)) {
339 CRM_Core_Error::statusBounce(ts('Your profile is not saved and Account is not created.'));
340 }
341 }
342 }
343
186c9c17 344 /**
c490a46a 345 * @param array $params
186c9c17
EM
346 * @param string $type
347 *
348 * @return bool
349 */
00be9182 350 public static function _fillCommonParams(&$params, $type = 'paypal') {
6a488035
TO
351 if (array_key_exists('transaction', $params)) {
352 $transaction = &$params['transaction'];
353 }
354 else {
355 $transaction = &$params;
356 }
357
358 $params['contact_type'] = 'Individual';
359
360 $billingLocTypeId = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_LocationType', 'Billing', 'id', 'name');
361 if (!$billingLocTypeId) {
362 $billingLocTypeId = 1;
363 }
364 if (!CRM_Utils_System::isNull($params['address'])) {
365 $params['address'][1]['is_primary'] = 1;
366 $params['address'][1]['location_type_id'] = $billingLocTypeId;
367 }
368 if (!CRM_Utils_System::isNull($params['email'])) {
369 $params['email'] = array(
874c9be7 370 1 => array(
353ffa53 371 'email' => $params['email'],
6a488035 372 'location_type_id' => $billingLocTypeId,
ae5ffbb7 373 ),
353ffa53 374 );
6a488035
TO
375 }
376
377 if (isset($transaction['trxn_id'])) {
378 // set error message if transaction has already been processed.
379 $contribution = new CRM_Contribute_DAO_Contribution();
380 $contribution->trxn_id = $transaction['trxn_id'];
381 if ($contribution->find(TRUE)) {
382 $params['error'][] = ts('transaction already processed.');
383 }
384 }
385 else {
386 // generate a new transaction id, if not already exist
387 $transaction['trxn_id'] = md5(uniqid(rand(), TRUE));
388 }
389
481a74f4 390 if (!isset($transaction['financial_type_id'])) {
6a488035
TO
391 $contributionTypes = array_keys(CRM_Contribute_PseudoConstant::financialType());
392 $transaction['financial_type_id'] = $contributionTypes[0];
393 }
394
395 if (($type == 'paypal') && (!isset($transaction['net_amount']))) {
396 $transaction['net_amount'] = $transaction['total_amount'] - CRM_Utils_Array::value('fee_amount', $transaction, 0);
397 }
398
399 if (!isset($transaction['invoice_id'])) {
400 $transaction['invoice_id'] = $transaction['trxn_id'];
401 }
402
403 $source = ts('ContributionProcessor: %1 API',
404 array(1 => ucfirst($type))
405 );
406 if (isset($transaction['source'])) {
407 $transaction['source'] = $source . ':: ' . $transaction['source'];
408 }
409 else {
410 $transaction['source'] = $source;
411 }
412
413 return TRUE;
414 }
415
186c9c17 416 /**
100fef9d 417 * @param int $contactID
186c9c17
EM
418 *
419 * @return mixed
420 */
00be9182 421 public static function getFirstLastDetails($contactID) {
6a488035
TO
422 static $_cache;
423
424 if (!$_cache) {
425 $_cache = array();
426 }
427
428 if (!isset($_cache[$contactID])) {
429 $sql = "
430SELECT total_amount, receive_date
431FROM civicrm_contribution c
432WHERE contact_id = %1
433ORDER BY receive_date ASC
434LIMIT 1
435";
436 $params = array(1 => array($contactID, 'Integer'));
437
438 $dao = CRM_Core_DAO::executeQuery($sql, $params);
439 $details = array(
440 'first' => NULL,
441 'last' => NULL,
442 );
443 if ($dao->fetch()) {
444 $details['first'] = array(
445 'total_amount' => $dao->total_amount,
446 'receive_date' => $dao->receive_date,
447 );
448 }
449
450 // flip asc and desc to get the last query
451 $sql = str_replace('ASC', 'DESC', $sql);
452 $dao = CRM_Core_DAO::executeQuery($sql, $params);
453 if ($dao->fetch()) {
454 $details['last'] = array(
455 'total_amount' => $dao->total_amount,
456 'receive_date' => $dao->receive_date,
457 );
458 }
459
460 $_cache[$contactID] = $details;
461 }
462 return $_cache[$contactID];
463 }
9d1c9154 464
465 /**
466 * Calculate the tax amount based on given tax rate.
467 *
014c4014
TO
468 * @param float $amount
469 * Amount of field.
470 * @param float $taxRate
471 * Tax rate of selected financial account for field.
9d1c9154 472 *
a6c01b45
CW
473 * @return array
474 * array of tax amount
9d1c9154 475 *
9d1c9154 476 */
477 public static function calculateTaxAmount($amount, $taxRate) {
478 $taxAmount = array();
c21d6df8 479 $taxAmount['tax_amount'] = round(($taxRate / 100) * CRM_Utils_Rule::cleanMoney($amount), 2);
9d1c9154 480
481 return $taxAmount;
482 }
96025800 483
6a488035 484}