CRM-12457
[civicrm-core.git] / CRM / Contribute / Import / Parser / Contribution.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.3 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2013 |
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 +--------------------------------------------------------------------+
26*/
27
28/**
29 *
30 * @package CRM
31 * @copyright CiviCRM LLC (c) 2004-2013
32 * $Id$
33 *
34 */
35
36/**
37 * class to parse contribution csv files
38 */
39class CRM_Contribute_Import_Parser_Contribution extends CRM_Contribute_Import_Parser {
40
41 protected $_mapperKeys;
42
43 private $_contactIdIndex;
44 private $_totalAmountIndex;
45 private $_contributionTypeIndex;
46
47 protected $_mapperSoftCredit;
48 //protected $_mapperPhoneType;
49
50 /**
ceb10dc7 51 * Array of successfully imported contribution id's
6a488035
TO
52 *
53 * @array
54 */
55 protected $_newContributions;
56
57 /**
58 * class constructor
59 */
60 function __construct(&$mapperKeys, $mapperSoftCredit = NULL, $mapperPhoneType = NULL) {
61 parent::__construct();
62 $this->_mapperKeys = &$mapperKeys;
63 $this->_mapperSoftCredit = &$mapperSoftCredit;
64 }
65
66 /**
67 * the initializer code, called before the processing
68 *
69 * @return void
70 * @access public
71 */
72 function init() {
73 $fields = CRM_Contribute_BAO_Contribution::importableFields($this->_contactType, FALSE);
74
75 $fields = array_merge($fields,
76 array('soft_credit' => array('title' => ts('Soft Credit'),
77 'softCredit' => TRUE,
78 'headerPattern' => '/Soft Credit/i',
79 ))
80 );
81
82 // add pledge fields only if its is enabled
83 if (CRM_Core_Permission::access('CiviPledge')) {
84 $pledgeFields = array('pledge_payment' => array('title' => ts('Pledge Payment'),
85 'headerPattern' => '/Pledge Payment/i',
86 ),
87 'pledge_id' => array('title' => ts('Pledge ID'),
88 'headerPattern' => '/Pledge ID/i',
89 ),
90 );
91
92 $fields = array_merge($fields, $pledgeFields);
93 }
94 foreach ($fields as $name => $field) {
95 $field['type'] = CRM_Utils_Array::value('type', $field, CRM_Utils_Type::T_INT);
96 $field['dataPattern'] = CRM_Utils_Array::value('dataPattern', $field, '//');
97 $field['headerPattern'] = CRM_Utils_Array::value('headerPattern', $field, '//');
98 $this->addField($name, $field['title'], $field['type'], $field['headerPattern'], $field['dataPattern']);
99 }
100
101 $this->_newContributions = array();
102
103 $this->setActiveFields($this->_mapperKeys);
104 $this->setActiveFieldSoftCredit($this->_mapperSoftCredit);
105
106 // FIXME: we should do this in one place together with Form/MapField.php
107 $this->_contactIdIndex = -1;
108 $this->_totalAmountIndex = -1;
109 $this->_contributionTypeIndex = -1;
110
111 $index = 0;
112 foreach ($this->_mapperKeys as $key) {
113 switch ($key) {
114 case 'contribution_contact_id':
115 $this->_contactIdIndex = $index;
116 break;
117
118 case 'total_amount':
119 $this->_totalAmountIndex = $index;
120 break;
121
122 case 'financial_type':
123 $this->_contributionTypeIndex = $index;
124 break;
125 }
126 $index++;
127 }
128 }
129
130 /**
131 * handle the values in mapField mode
132 *
133 * @param array $values the array of values belonging to this line
134 *
135 * @return boolean
136 * @access public
137 */
138 function mapField(&$values) {
139 return CRM_Contribute_Import_Parser::VALID;
140 }
141
142 /**
143 * handle the values in preview mode
144 *
145 * @param array $values the array of values belonging to this line
146 *
147 * @return boolean the result of this processing
148 * @access public
149 */
150 function preview(&$values) {
151 return $this->summary($values);
152 }
153
154 /**
155 * handle the values in summary mode
156 *
157 * @param array $values the array of values belonging to this line
158 *
159 * @return boolean the result of this processing
160 * @access public
161 */
162 function summary(&$values) {
163 $erroneousField = NULL;
164 $response = $this->setActiveFieldValues($values, $erroneousField);
165
166 $params = &$this->getActiveFieldParams();
167 $errorMessage = NULL;
168
169 //for date-Formats
170 $session = CRM_Core_Session::singleton();
171 $dateType = $session->get('dateTypes');
172 foreach ($params as $key => $val) {
173 if ($val) {
174 switch ($key) {
175 case 'receive_date':
176 if ($dateValue = CRM_Utils_Date::formatDate($params[$key], $dateType)) {
177 $params[$key] = $dateValue;
178 }
179 else {
180 CRM_Import_Parser_Contact::addToErrorMsg('Receive Date', $errorMessage);
181 }
182 break;
183
184 case 'cancel_date':
185 if ($dateValue = CRM_Utils_Date::formatDate($params[$key], $dateType)) {
ceb10dc7 186 $params[$key] = $dateValue;
6a488035
TO
187 }
188 else {
189 CRM_Import_Parser_Contact::addToErrorMsg('Cancel Date', $errorMessage);
190 }
191 break;
192
193 case 'receipt_date':
194 if ($dateValue = CRM_Utils_Date::formatDate($params[$key], $dateType)) {
195 $params[$key] = $dateValue;
196 }
197 else {
198 CRM_Import_Parser_Contact::addToErrorMsg('Receipt date', $errorMessage);
199 }
200 break;
201
202 case 'thankyou_date':
203 if ($dateValue = CRM_Utils_Date::formatDate($params[$key], $dateType)) {
204 $params[$key] = $dateValue;
205 }
206 else {
207 CRM_Import_Parser_Contact::addToErrorMsg('Thankyou Date', $errorMessage);
208 }
209 break;
210 }
211 }
212 }
213 //date-Format part ends
214
215 $params['contact_type'] = 'Contribution';
216
217 //checking error in custom data
218 CRM_Import_Parser_Contact::isErrorInCustomData($params, $errorMessage);
219
220 if ($errorMessage) {
221 $tempMsg = "Invalid value for field(s) : $errorMessage";
222 array_unshift($values, $tempMsg);
223 $errorMessage = NULL;
224 return CRM_Contribute_Import_Parser::ERROR;
225 }
226
227 return CRM_Contribute_Import_Parser::VALID;
228 }
229
230 /**
231 * handle the values in import mode
232 *
233 * @param int $onDuplicate the code for what action to take on duplicates
234 * @param array $values the array of values belonging to this line
235 *
236 * @return boolean the result of this processing
237 * @access public
238 */
239 function import($onDuplicate, &$values) {
240 // first make sure this is a valid line
241 $response = $this->summary($values);
242 if ($response != CRM_Contribute_Import_Parser::VALID) {
243 return $response;
244 }
245
246 $params = &$this->getActiveFieldParams();
247 $formatted = array('version' => 3);
248
249 // don't add to recent items, CRM-4399
250 $formatted['skipRecentView'] = TRUE;
251
252 //for date-Formats
253 $session = CRM_Core_Session::singleton();
254 $dateType = $session->get('dateTypes');
255
256 $customFields = CRM_Core_BAO_CustomField::getFields(CRM_Utils_Array::value('contact_type', $params));
257
258 foreach ($params as $key => $val) {
259 if ($val) {
260 switch ($key) {
ad8eaec9 261 case 'receive_date':
262 case 'cancel_date':
263 case 'receipt_date':
264 case 'thankyou_date':
265 $params[$key] = CRM_Utils_Date::formatDate($params[$key], $dateType);
266 break;
267 case 'pledge_payment':
268 $params[$key] = CRM_Utils_String::strtobool($val);
269 break;
6a488035
TO
270 }
271 if ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) {
272 if ($customFields[$customFieldID]['data_type'] == 'Date') {
273 CRM_Import_Parser_Contact::formatCustomDate($params, $formatted, $dateType, $key);
274 unset($params[$key]);
275 }
276 elseif ($customFields[$customFieldID]['data_type'] == 'Boolean') {
277 $params[$key] = CRM_Utils_String::strtoboolstr($val);
278 }
279 }
280 }
281 }
282 //date-Format part ends
283
284 static $indieFields = NULL;
285 if ($indieFields == NULL) {
286 $tempIndieFields = CRM_Contribute_DAO_Contribution::import();
287 $indieFields = $tempIndieFields;
288 }
289
290 $paramValues = array();
291 foreach ($params as $key => $field) {
292 if ($field == NULL || $field === '') {
293 continue;
294 }
295 $paramValues[$key] = $field;
296 }
297
298 //import contribution record according to select contact type
299 if ($onDuplicate == CRM_Contribute_Import_Parser::DUPLICATE_SKIP &&
300 (CRM_Utils_Array::value('contribution_contact_id', $paramValues) ||
301 CRM_Utils_Array::value('external_identifier', $paramValues)
302 )
303 ) {
304 $paramValues['contact_type'] = $this->_contactType;
305 }
306 elseif ($onDuplicate == CRM_Contribute_Import_Parser::DUPLICATE_UPDATE &&
307 ($paramValues['contribution_id'] || $values['trxn_id'] || $paramValues['invoice_id'])
308 ) {
309 $paramValues['contact_type'] = $this->_contactType;
310 }
311 elseif (!empty($params['soft_credit'])) {
312 $paramValues['contact_type'] = $this->_contactType;
313 }
314 elseif (CRM_Utils_Array::value('pledge_payment', $paramValues)) {
315 $paramValues['contact_type'] = $this->_contactType;
316 }
317
318 //need to pass $onDuplicate to check import mode.
319 if (CRM_Utils_Array::value('pledge_payment', $paramValues)) {
320 $paramValues['onDuplicate'] = $onDuplicate;
321 }
322 require_once 'CRM/Utils/DeprecatedUtils.php';
323 $formatError = _civicrm_api3_deprecated_formatted_param($paramValues, $formatted, TRUE);
324
325 if ($formatError) {
326 array_unshift($values, $formatError['error_message']);
327 if (CRM_Utils_Array::value('error_data', $formatError) == 'soft_credit') {
328 return CRM_Contribute_Import_Parser::SOFT_CREDIT_ERROR;
329 }
330 elseif (CRM_Utils_Array::value('error_data', $formatError) == 'pledge_payment') {
331 return CRM_Contribute_Import_Parser::PLEDGE_PAYMENT_ERROR;
332 }
333 return CRM_Contribute_Import_Parser::ERROR;
334 }
335
336 if ($onDuplicate != CRM_Contribute_Import_Parser::DUPLICATE_UPDATE) {
337 $formatted['custom'] = CRM_Core_BAO_CustomField::postProcess($formatted,
338 CRM_Core_DAO::$_nullObject,
339 NULL,
340 'Contribution'
341 );
342 }
343 else {
344 //fix for CRM-2219 - Update Contribution
345 // onDuplicate == CRM_Contribute_Import_Parser::DUPLICATE_UPDATE
346 if (CRM_Utils_Array::value('invoice_id',$paramValues) ||
347 CRM_Utils_Array::value('trxn_id', $paramValues) || $paramValues['contribution_id']) {
348 $dupeIds = array(
349 'id' => CRM_Utils_Array::value('contribution_id', $paramValues),
350 'trxn_id' => CRM_Utils_Array::value('trxn_id', $paramValues),
351 'invoice_id' => CRM_Utils_Array::value('invoice_id', $paramValues),
352 );
353
354 $ids['contribution'] = CRM_Contribute_BAO_Contribution::checkDuplicateIds($dupeIds);
355
356 if ($ids['contribution']) {
357 $formatted['id'] = $ids['contribution'];
358 $formatted['custom'] = CRM_Core_BAO_CustomField::postProcess($formatted,
359 CRM_Core_DAO::$_nullObject,
360 $formatted['id'],
361 'Contribution'
362 );
363 //process note
364 if (CRM_Utils_Array::value('note', $paramValues)) {
365 $noteID = array();
366 $contactID = CRM_Core_DAO::getFieldValue('CRM_Contribute_DAO_Contribution', $ids['contribution'], 'contact_id');
367 $daoNote = new CRM_Core_BAO_Note();
368 $daoNote->entity_table = 'civicrm_contribution';
369 $daoNote->entity_id = $ids['contribution'];
370 if ($daoNote->find(TRUE)) {
371 $noteID['id'] = $daoNote->id;
372 }
373
374 $noteParams = array(
375 'entity_table' => 'civicrm_contribution',
376 'note' => $paramValues['note'],
377 'entity_id' => $ids['contribution'],
378 'contact_id' => $contactID,
379 );
380 CRM_Core_BAO_Note::add($noteParams, $noteID);
381 unset($formatted['note']);
382 }
383
384 //need to check existing soft credit contribution, CRM-3968
385 if (CRM_Utils_Array::value('soft_credit_to', $formatted)) {
386 $dupeSoftCredit = array(
387 'contact_id' => $formatted['soft_credit_to'],
388 'contribution_id' => $ids['contribution'],
389 );
390 $existingSoftCredit = CRM_Contribute_BAO_Contribution::getSoftContribution($dupeSoftCredit);
391 if (CRM_Utils_Array::value('soft_credit_id', $existingSoftCredit)) {
392 $formatted['softID'] = $existingSoftCredit['soft_credit_id'];
393 }
394 }
395
396 $newContribution = CRM_Contribute_BAO_Contribution::create($formatted, $ids);
ceb10dc7 397
6a488035
TO
398 $this->_newContributions[] = $newContribution->id;
399
400 //return soft valid since we need to show how soft credits were added
401 if (CRM_Utils_Array::value('soft_credit_to', $formatted)) {
402 return CRM_Contribute_Import_Parser::SOFT_CREDIT;
403 }
404
405 // process pledge payment assoc w/ the contribution
406 return self::processPledgePayments($formatted);
407
408 return CRM_Contribute_Import_Parser::VALID;
409 }
410 else {
411 $labels = array(
412 'id' => 'Contribution ID',
413 'trxn_id' => 'Transaction ID',
414 'invoice_id' => 'Invoice ID',
415 );
416 foreach ($dupeIds as $k => $v) {
417 if ($v) {
418 $errorMsg[] = "$labels[$k] $v";
419 }
420 }
421 $errorMsg = implode(' AND ', $errorMsg);
422 array_unshift($values, 'Matching Contribution record not found for ' . $errorMsg . '. Row was skipped.');
423 return CRM_Contribute_Import_Parser::ERROR;
424 }
425 }
426 }
427
428 if ($this->_contactIdIndex < 0) {
429 // set the contact type if its not set
430 if (!isset($paramValues['contact_type'])) {
431 $paramValues['contact_type'] = $this->_contactType;
432 }
433
434 $paramValues['version'] = 3;
435 //retrieve contact id using contact dedupe rule
436 require_once 'CRM/Utils/DeprecatedUtils.php';
437 $error = _civicrm_api3_deprecated_check_contact_dedupe($paramValues);
438
439 if (CRM_Core_Error::isAPIError($error, CRM_Core_ERROR::DUPLICATE_CONTACT)) {
440 $matchedIDs = explode(',', $error['error_message']['params'][0]);
441 if (count($matchedIDs) > 1) {
442 array_unshift($values, 'Multiple matching contact records detected for this row. The contribution was not imported');
443 return CRM_Contribute_Import_Parser::ERROR;
444 }
445 else {
446 $cid = $matchedIDs[0];
447 $formatted['contact_id'] = $cid;
448
449 $newContribution = civicrm_api('contribution', 'create', $formatted);
450 if (civicrm_error($newContribution)) {
451 if (is_array($newContribution['error_message'])) {
452 array_unshift($values, $newContribution['error_message']['message']);
453 if ($newContribution['error_message']['params'][0]) {
454 return CRM_Contribute_Import_Parser::DUPLICATE;
455 }
456 }
457 else {
458 array_unshift($values, $newContribution['error_message']);
459 return CRM_Contribute_Import_Parser::ERROR;
460 }
461 }
462
463 $this->_newContributions[] = $newContribution['id'];
464 $formatted['contribution_id'] = $newContribution['id'];
465
466 //return soft valid since we need to show how soft credits were added
467 if (CRM_Utils_Array::value('soft_credit_to', $formatted)) {
468 return CRM_Contribute_Import_Parser::SOFT_CREDIT;
469 }
470
471 // process pledge payment assoc w/ the contribution
472 return self::processPledgePayments($formatted);
473
474 return CRM_Contribute_Import_Parser::VALID;
475 }
476 }
477 else {
478 // Using new Dedupe rule.
479 $ruleParams = array(
480 'contact_type' => $this->_contactType,
481 'used' => 'Unsupervised',
482 );
483 $fieldsArray = CRM_Dedupe_BAO_Rule::dedupeRuleFields($ruleParams);
484
485 foreach ($fieldsArray as $value) {
486 if (array_key_exists(trim($value), $params)) {
487 $paramValue = $params[trim($value)];
488 if (is_array($paramValue)) {
489 $disp .= $params[trim($value)][0][trim($value)] . " ";
490 }
491 else {
492 $disp .= $params[trim($value)] . " ";
493 }
494 }
495 }
496
497 if (CRM_Utils_Array::value('external_identifier', $params)) {
498 if ($disp) {
499 $disp .= "AND {$params['external_identifier']}";
500 }
501 else {
502 $disp = $params['external_identifier'];
503 }
504 }
505
506 array_unshift($values, 'No matching Contact found for (' . $disp . ')');
507 return CRM_Contribute_Import_Parser::ERROR;
508 }
509 }
510 else {
511 if (CRM_Utils_Array::value('external_identifier', $paramValues)) {
512 $checkCid = new CRM_Contact_DAO_Contact();
513 $checkCid->external_identifier = $paramValues['external_identifier'];
514 $checkCid->find(TRUE);
515 if ($checkCid->id != $formatted['contact_id']) {
516 array_unshift($values, 'Mismatch of External identifier :' . $paramValues['external_identifier'] . ' and Contact Id:' . $formatted['contact_id']);
517 return CRM_Contribute_Import_Parser::ERROR;
518 }
519 }
520 $newContribution = civicrm_api('contribution', 'create', $formatted);
521 if (civicrm_error($newContribution)) {
522 if (is_array($newContribution['error_message'])) {
523 array_unshift($values, $newContribution['error_message']['message']);
524 if ($newContribution['error_message']['params'][0]) {
525 return CRM_Contribute_Import_Parser::DUPLICATE;
526 }
527 }
528 else {
529 array_unshift($values, $newContribution['error_message']);
530 return CRM_Contribute_Import_Parser::ERROR;
531 }
532 }
533
534 $this->_newContributions[] = $newContribution['id'];
535 $formatted['contribution_id'] = $newContribution['id'];
536
537 //return soft valid since we need to show how soft credits were added
538 if (CRM_Utils_Array::value('soft_credit_to', $formatted)) {
539 return CRM_Contribute_Import_Parser::SOFT_CREDIT;
540 }
541
542 // process pledge payment assoc w/ the contribution
543 return self::processPledgePayments($formatted);
544
545 return CRM_Contribute_Import_Parser::VALID;
546 }
547 }
548
549 /**
550 * Function to process pledge payments
551 */
552 function processPledgePayments(&$formatted) {
553 if (CRM_Utils_Array::value('pledge_payment_id', $formatted) &&
554 CRM_Utils_Array::value('pledge_id', $formatted)
555 ) {
556 //get completed status
557 $completeStatusID = CRM_Core_OptionGroup::getValue('contribution_status', 'Completed', 'name');
558
559 //need to update payment record to map contribution_id
560 CRM_Core_DAO::setFieldValue('CRM_Pledge_DAO_PledgePayment', $formatted['pledge_payment_id'],
561 'contribution_id', $formatted['contribution_id']
562 );
563
564 CRM_Pledge_BAO_PledgePayment::updatePledgePaymentStatus($formatted['pledge_id'],
565 array($formatted['pledge_payment_id']),
566 $completeStatusID,
567 NULL,
568 $formatted['total_amount']
569 );
570
571 return CRM_Contribute_Import_Parser::PLEDGE_PAYMENT;
572 }
573 }
574
575 /**
ceb10dc7 576 * Get the array of successfully imported contribution id's
6a488035
TO
577 *
578 * @return array
579 * @access public
580 */
581 function &getImportedContributions() {
582 return $this->_newContributions;
583 }
584
585 /**
586 * the initializer code, called before the processing
587 *
588 * @return void
589 * @access public
590 */
591 function fini() {}
592}
593