3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
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. |
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. |
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 +--------------------------------------------------------------------+
31 * @copyright CiviCRM LLC (c) 2004-2014
37 require_once 'api/api.php';
40 * class to parse membership csv files
42 class CRM_Member_Import_Parser_Membership
extends CRM_Member_Import_Parser
{
44 protected $_mapperKeys;
46 private $_contactIdIndex;
47 private $_totalAmountIndex;
48 private $_membershipTypeIndex;
49 private $_membershipStatusIndex;
52 * Array of successfully imported membership id's
56 protected $_newMemberships;
61 function __construct(&$mapperKeys, $mapperLocType = NULL, $mapperPhoneType = NULL) {
62 parent
::__construct();
63 $this->_mapperKeys
= &$mapperKeys;
67 * the initializer code, called before the processing
73 $fields = CRM_Member_BAO_Membership
::importableFields($this->_contactType
, FALSE);
75 foreach ($fields as $name => $field) {
76 $field['type'] = CRM_Utils_Array
::value('type', $field, CRM_Utils_Type
::T_INT
);
77 $field['dataPattern'] = CRM_Utils_Array
::value('dataPattern', $field, '//');
78 $field['headerPattern'] = CRM_Utils_Array
::value('headerPattern', $field, '//');
79 $this->addField($name, $field['title'], $field['type'], $field['headerPattern'], $field['dataPattern']);
82 $this->_newMemberships
= array();
84 $this->setActiveFields($this->_mapperKeys
);
86 // FIXME: we should do this in one place together with Form/MapField.php
87 $this->_contactIdIndex
= -1;
88 $this->_membershipTypeIndex
= -1;
89 $this->_membershipStatusIndex
= -1;
92 foreach ($this->_mapperKeys
as $key) {
94 case 'membership_contact_id':
95 $this->_contactIdIndex
= $index;
98 case 'membership_type_id':
99 $this->_membershipTypeIndex
= $index;
103 $this->_membershipStatusIndex
= $index;
111 * handle the values in mapField mode
113 * @param array $values the array of values belonging to this line
118 function mapField(&$values) {
119 return CRM_Import_Parser
::VALID
;
123 * handle the values in preview mode
125 * @param array $values the array of values belonging to this line
127 * @return boolean the result of this processing
130 function preview(&$values) {
131 return $this->summary($values);
135 * handle the values in summary mode
137 * @param array $values the array of values belonging to this line
139 * @return boolean the result of this processing
142 function summary(&$values) {
143 $erroneousField = NULL;
144 $response = $this->setActiveFieldValues($values, $erroneousField);
146 $errorRequired = FALSE;
148 if ($this->_membershipTypeIndex
< 0) {
149 $errorRequired = TRUE;
152 $errorRequired = !CRM_Utils_Array
::value($this->_membershipTypeIndex
, $values);
155 if ($errorRequired) {
156 array_unshift($values, ts('Missing required fields'));
157 return CRM_Import_Parser
::ERROR
;
160 $params = &$this->getActiveFieldParams();
161 $errorMessage = NULL;
163 //To check whether start date or join date is provided
164 if (empty($params['membership_start_date']) && empty($params['join_date'])) {
165 $errorMessage = 'Membership Start Date is required to create a memberships.';
166 CRM_Contact_Import_Parser_Contact
::addToErrorMsg('Start Date', $errorMessage);
171 $session = CRM_Core_Session
::singleton();
172 $dateType = $session->get('dateTypes');
173 foreach ($params as $key => $val) {
178 if (CRM_Utils_Date
::convertToDefaultDate($params, $dateType, $key)) {
179 if (!CRM_Utils_Rule
::date($params[$key])) {
180 CRM_Contact_Import_Parser_Contact
::addToErrorMsg('Member Since', $errorMessage);
184 CRM_Contact_Import_Parser_Contact
::addToErrorMsg('Member Since', $errorMessage);
188 case 'membership_start_date':
189 if (CRM_Utils_Date
::convertToDefaultDate($params, $dateType, $key)) {
190 if (!CRM_Utils_Rule
::date($params[$key])) {
191 CRM_Contact_Import_Parser_Contact
::addToErrorMsg('Start Date', $errorMessage);
195 CRM_Contact_Import_Parser_Contact
::addToErrorMsg('Start Date', $errorMessage);
199 case 'membership_end_date':
200 if (CRM_Utils_Date
::convertToDefaultDate($params, $dateType, $key)) {
201 if (!CRM_Utils_Rule
::date($params[$key])) {
202 CRM_Contact_Import_Parser_Contact
::addToErrorMsg('End date', $errorMessage);
206 CRM_Contact_Import_Parser_Contact
::addToErrorMsg('End date', $errorMessage);
210 case 'membership_type_id':
211 $membershipTypes = CRM_Member_PseudoConstant
::membershipType();
212 if (!CRM_Utils_Array
::crmInArray($val, $membershipTypes) &&
213 !array_key_exists($val, $membershipTypes)
215 CRM_Contact_Import_Parser_Contact
::addToErrorMsg('Membership Type', $errorMessage);
220 if (!CRM_Utils_Array
::crmInArray($val, CRM_Member_PseudoConstant
::membershipStatus())) {
221 CRM_Contact_Import_Parser_Contact
::addToErrorMsg('Membership Status', $errorMessage);
226 if (!CRM_Utils_Rule
::email($val)) {
227 CRM_Contact_Import_Parser_Contact
::addToErrorMsg('Email Address', $errorMessage);
232 //date-Format part ends
234 $params['contact_type'] = 'Membership';
236 //checking error in custom data
237 CRM_Contact_Import_Parser_Contact
::isErrorInCustomData($params, $errorMessage);
240 $tempMsg = "Invalid value for field(s) : $errorMessage";
241 array_unshift($values, $tempMsg);
242 $errorMessage = NULL;
243 return CRM_Import_Parser
::ERROR
;
246 return CRM_Import_Parser
::VALID
;
250 * handle the values in import mode
252 * @param int $onDuplicate the code for what action to take on duplicates
253 * @param array $values the array of values belonging to this line
255 * @return boolean the result of this processing
258 function import($onDuplicate, &$values) {
260 // first make sure this is a valid line
261 $response = $this->summary($values);
262 if ($response != CRM_Import_Parser
::VALID
) {
266 $params = &$this->getActiveFieldParams();
268 //assign join date equal to start date if join date is not provided
269 if (empty($params['join_date']) && !empty($params['membership_start_date'])) {
270 $params['join_date'] = $params['membership_start_date'];
273 $session = CRM_Core_Session
::singleton();
274 $dateType = $session->get('dateTypes');
275 $formatted = array();
276 $customFields = CRM_Core_BAO_CustomField
::getFields(CRM_Utils_Array
::value('contact_type', $params));
278 // don't add to recent items, CRM-4399
279 $formatted['skipRecentView'] = TRUE;
281 'join_date' => ts('Member Since'),
282 'membership_start_date' => ts('Start Date'),
283 'membership_end_date' => ts('End Date'),
285 foreach ($params as $key => $val) {
289 case 'membership_start_date':
290 case 'membership_end_date':
291 if (CRM_Utils_Date
::convertToDefaultDate($params, $dateType, $key)) {
292 if (!CRM_Utils_Rule
::date($params[$key])) {
293 CRM_Contact_Import_Parser_Contact
::addToErrorMsg($dateLabels[$key], $errorMessage);
297 CRM_Contact_Import_Parser_Contact
::addToErrorMsg($dateLabels[$key], $errorMessage);
301 case 'membership_type_id':
302 if (!is_numeric($val)) {
303 unset($params['membership_type_id']);
304 $params['membership_type'] = $val;
309 if (!is_numeric($val)) {
310 unset($params['status_id']);
311 $params['membership_status'] = $val;
316 $params[$key] = CRM_Utils_String
::strtobool($val);
319 if ($customFieldID = CRM_Core_BAO_CustomField
::getKeyID($key)) {
320 if ( $customFields[$customFieldID]['data_type'] == 'Date' ) {
321 CRM_Contact_Import_Parser_Contact
::formatCustomDate($params, $formatted, $dateType, $key);
322 unset($params[$key]);
323 } else if ( $customFields[$customFieldID]['data_type'] == 'Boolean' ) {
324 $params[$key] = CRM_Utils_String
::strtoboolstr($val);
329 //date-Format part ends
331 static $indieFields = NULL;
332 if ($indieFields == NULL) {
333 $tempIndieFields = CRM_Member_DAO_Membership
::import();
334 $indieFields = $tempIndieFields;
337 $formatValues = array();
338 foreach ($params as $key => $field) {
339 if ($field == NULL ||
$field === '') {
343 $formatValues[$key] = $field;
346 //format params to meet api v2 requirements.
347 //@todo find a way to test removing this formatting
348 $formatError = $this->membership_format_params($formatValues, $formatted, TRUE);
350 if ($onDuplicate != CRM_Import_Parser
::DUPLICATE_UPDATE
) {
351 $formatted['custom'] = CRM_Core_BAO_CustomField
::postProcess($formatted,
352 CRM_Core_DAO
::$_nullObject,
358 //fix for CRM-2219 Update Membership
359 // onDuplicate == CRM_Import_Parser::DUPLICATE_UPDATE
360 if (!empty($formatted['is_override']) && empty($formatted['status_id'])) {
361 array_unshift($values, 'Required parameter missing: Status');
362 return CRM_Import_Parser
::ERROR
;
365 if (!empty($formatValues['membership_id'])) {
366 $dao = new CRM_Member_BAO_Membership();
367 $dao->id
= $formatValues['membership_id'];
368 $dates = array('join_date', 'start_date', 'end_date');
369 foreach ($dates as $v) {
370 if (empty($formatted[$v])) {
371 $formatted[$v] = CRM_Core_DAO
::getFieldValue('CRM_Member_DAO_Membership', $formatValues['membership_id'], $v);
375 $formatted['custom'] = CRM_Core_BAO_CustomField
::postProcess($formatted,
376 CRM_Core_DAO
::$_nullObject,
377 $formatValues['membership_id'],
380 if ($dao->find(TRUE)) {
382 'membership' => $formatValues['membership_id'],
383 'userId' => $session->get('userID'),
386 if (empty($params['line_item']) && !empty($formatted['membership_type_id'])) {
387 CRM_Price_BAO_LineItem
::getLineItemArray($formatted, NULL, 'membership', $formatted['membership_type_id']);
390 $newMembership = CRM_Member_BAO_Membership
::create($formatted, $ids, TRUE);
391 if (civicrm_error($newMembership)) {
392 array_unshift($values, $newMembership['is_error'] . ' for Membership ID ' . $formatValues['membership_id'] . '. Row was skipped.');
393 return CRM_Import_Parser
::ERROR
;
396 $this->_newMemberships
[] = $newMembership->id
;
397 return CRM_Import_Parser
::VALID
;
401 array_unshift($values, 'Matching Membership record not found for Membership ID ' . $formatValues['membership_id'] . '. Row was skipped.');
402 return CRM_Import_Parser
::ERROR
;
408 $startDate = CRM_Utils_Date
::customFormat(CRM_Utils_Array
::value('start_date', $formatted), '%Y-%m-%d');
409 $endDate = CRM_Utils_Date
::customFormat(CRM_Utils_Array
::value('end_date', $formatted), '%Y-%m-%d');
410 $joinDate = CRM_Utils_Date
::customFormat(CRM_Utils_Array
::value('join_date', $formatted), '%Y-%m-%d');
412 if ($this->_contactIdIndex
< 0) {
414 //retrieve contact id using contact dedupe rule
415 $formatValues['contact_type'] = $this->_contactType
;
416 $formatValues['version'] = 3;
417 require_once 'CRM/Utils/DeprecatedUtils.php';
418 $error = _civicrm_api3_deprecated_check_contact_dedupe($formatValues);
420 if (CRM_Core_Error
::isAPIError($error, CRM_Core_ERROR
::DUPLICATE_CONTACT
)) {
421 $matchedIDs = explode(',', $error['error_message']['params'][0]);
422 if (count($matchedIDs) > 1) {
423 array_unshift($values, 'Multiple matching contact records detected for this row. The membership was not imported');
424 return CRM_Import_Parser
::ERROR
;
427 $cid = $matchedIDs[0];
428 $formatted['contact_id'] = $cid;
431 $calcDates = CRM_Member_BAO_MembershipType
::getDatesForMembershipType($formatted['membership_type_id'],
436 self
::formattedDates($calcDates, $formatted);
438 //fix for CRM-3570, exclude the statuses those having is_admin = 1
439 //now user can import is_admin if is override is true.
440 $excludeIsAdmin = FALSE;
441 if (empty($formatted['is_override'])) {
442 $formatted['exclude_is_admin'] = $excludeIsAdmin = TRUE;
444 $calcStatus = CRM_Member_BAO_MembershipStatus
::getMembershipStatusByDate($startDate,
449 $formatted['membership_type_id'],
453 if (empty($formatted['status_id'])) {
454 $formatted['status_id'] = $calcStatus['id'];
456 elseif (empty($formatted['is_override'])) {
457 if (empty($calcStatus)) {
458 array_unshift($values, 'Status in import row (' . $formatValues['status_id'] . ') does not match calculated status based on your configured Membership Status Rules. Record was not imported.');
459 return CRM_Import_Parser
::ERROR
;
461 elseif ($formatted['status_id'] != $calcStatus['id']) {
462 //Status Hold" is either NOT mapped or is FALSE
463 array_unshift($values, 'Status in import row (' . $formatValues['status_id'] . ') does not match calculated status based on your configured Membership Status Rules (' . $calcStatus['name'] . '). Record was not imported.');
464 return CRM_Import_Parser
::ERROR
;
468 $newMembership = civicrm_api3('membership', 'create', $formatted);
470 $this->_newMemberships
[] = $newMembership['id'];
471 return CRM_Import_Parser
::VALID
;
475 // Using new Dedupe rule.
477 'contact_type' => $this->_contactType
,
478 'used' => 'Unsupervised',
480 $fieldsArray = CRM_Dedupe_BAO_Rule
::dedupeRuleFields($ruleParams);
483 foreach ($fieldsArray as $value) {
484 if (array_key_exists(trim($value), $params)) {
485 $paramValue = $params[trim($value)];
486 if (is_array($paramValue)) {
487 $disp .= $params[trim($value)][0][trim($value)] . " ";
490 $disp .= $params[trim($value)] . " ";
495 if (!empty($params['external_identifier'])) {
497 $disp .= "AND {$params['external_identifier']}";
500 $disp = $params['external_identifier'];
504 array_unshift($values, 'No matching Contact found for (' . $disp . ')');
505 return CRM_Import_Parser
::ERROR
;
509 if (!empty($formatValues['external_identifier'])) {
510 $checkCid = new CRM_Contact_DAO_Contact();
511 $checkCid->external_identifier
= $formatValues['external_identifier'];
512 $checkCid->find(TRUE);
513 if ($checkCid->id
!= $formatted['contact_id']) {
514 array_unshift($values, 'Mismatch of External identifier :' . $formatValues['external_identifier'] . ' and Contact Id:' . $formatted['contact_id']);
515 return CRM_Import_Parser
::ERROR
;
520 $calcDates = CRM_Member_BAO_MembershipType
::getDatesForMembershipType($formatted['membership_type_id'],
525 self
::formattedDates($calcDates, $formatted);
526 //end of date calculation part
528 //fix for CRM-3570, exclude the statuses those having is_admin = 1
529 //now user can import is_admin if is override is true.
530 $excludeIsAdmin = FALSE;
531 if (empty($formatted['is_override'])) {
532 $formatted['exclude_is_admin'] = $excludeIsAdmin = TRUE;
534 $calcStatus = CRM_Member_BAO_MembershipStatus
::getMembershipStatusByDate($startDate,
539 $formatted['membership_type_id'],
542 if (empty($formatted['status_id'])) {
543 $formatted['status_id'] = CRM_Utils_Array
::value('id', $calcStatus);
545 elseif (empty($formatted['is_override'])) {
546 if (empty($calcStatus)) {
547 array_unshift($values, 'Status in import row (' . CRM_Utils_Array
::value('status_id', $formatValues) . ') does not match calculated status based on your configured Membership Status Rules. Record was not imported.');
548 return CRM_Import_Parser
::ERROR
;
550 elseif ($formatted['status_id'] != $calcStatus['id']) {
551 //Status Hold" is either NOT mapped or is FALSE
552 array_unshift($values, 'Status in import row (' . CRM_Utils_Array
::value('status_id', $formatValues) . ') does not match calculated status based on your configured Membership Status Rules (' . $calcStatus['name'] . '). Record was not imported.');
553 return CRM_Import_Parser
::ERROR
;
557 $newMembership = civicrm_api3('membership', 'create', $formatted);
559 $this->_newMemberships
[] = $newMembership['id'];
560 return CRM_Import_Parser
::VALID
;
563 catch (Exception
$e) {
564 array_unshift($values, $e->getMessage());
565 return CRM_Import_Parser
::ERROR
;
570 * Get the array of successfully imported membership id's
575 function &getImportedMemberships() {
576 return $this->_newMemberships
;
580 * the initializer code, called before the processing
588 * to calculate join, start and end dates
590 * @param Array $calcDates array of dates returned by getDatesForMembershipType()
594 * @return Array formatted containing date values
598 function formattedDates($calcDates, &$formatted) {
605 foreach ($dates as $d) {
606 if (isset($formatted[$d]) &&
607 !CRM_Utils_System
::isNull($formatted[$d])
609 $formatted[$d] = CRM_Utils_Date
::isoToMysql($formatted[$d]);
611 elseif (isset($calcDates[$d])) {
612 $formatted[$d] = CRM_Utils_Date
::isoToMysql($calcDates[$d]);
618 * @deprecated - this function formats params according to v2 standards but
619 * need to be sure about the impact of not calling it so retaining on the import class
620 * take the input parameter list as specified in the data model and
621 * convert it into the same format that we use in QF and BAO object
623 * @param array $params Associative array of property name/value
624 * pairs to insert in new contact.
625 * @param array $values The reformatted properties that we can use internally
627 * @param array|bool $create Is the formatted Values array going to
628 * be used for CRM_Member_BAO_Membership:create()
631 * @return array|error
634 function membership_format_params($params, &$values, $create = FALSE) {
635 require_once 'api/v3/utils.php';
636 $fields = CRM_Member_DAO_Membership
::fields();
637 _civicrm_api3_store_values($fields, $params, $values);
639 $customFields = CRM_Core_BAO_CustomField
::getFields( 'Membership');
641 foreach ($params as $key => $value) {
642 // ignore empty values or empty arrays etc
643 if (CRM_Utils_System
::isNull($value)) {
647 //Handling Custom Data
648 if ($customFieldID = CRM_Core_BAO_CustomField
::getKeyID($key)) {
649 $values[$key] = $value;
650 $type = $customFields[$customFieldID]['html_type'];
651 if( $type == 'CheckBox' ||
$type == 'Multi-Select' ||
$type == 'AdvMulti-Select') {
652 $mulValues = explode( ',' , $value );
653 $customOption = CRM_Core_BAO_CustomOption
::getCustomOption($customFieldID, true);
654 $values[$key] = array();
655 foreach( $mulValues as $v1 ) {
656 foreach($customOption as $customValueID => $customLabel) {
657 $customValue = $customLabel['value'];
658 if (( strtolower($customLabel['label']) == strtolower(trim($v1)) ) ||
659 ( strtolower($customValue) == strtolower(trim($v1)) )) {
660 if ( $type == 'CheckBox' ) {
661 $values[$key][$customValue] = 1;
663 $values[$key][] = $customValue;
672 case 'membership_contact_id':
673 if (!CRM_Utils_Rule
::integer($value)) {
674 throw new Exception("contact_id not valid: $value");
676 $dao = new CRM_Core_DAO();
678 $svq = $dao->singleValueQuery("SELECT id FROM civicrm_contact WHERE id = $value",
682 throw new Exception("Invalid Contact ID: There is no contact record with contact_id = $value.");
684 $values['contact_id'] = $values['membership_contact_id'];
685 unset($values['membership_contact_id']);
688 case 'membership_type_id':
689 if (!CRM_Utils_Array
::value($value, CRM_Member_PseudoConstant
::membershipType())) {
690 throw new Exception('Invalid Membership Type Id');
692 $values[$key] = $value;
695 case 'membership_type':
696 $membershipTypeId = CRM_Utils_Array
::key(ucfirst($value),
697 CRM_Member_PseudoConstant
::membershipType()
699 if ($membershipTypeId) {
700 if (!empty($values['membership_type_id']) &&
701 $membershipTypeId != $values['membership_type_id']
703 throw new Exception('Mismatched membership Type and Membership Type Id');
707 throw new Exception('Invalid Membership Type');
709 $values['membership_type_id'] = $membershipTypeId;
713 if (!CRM_Utils_Array
::value($value, CRM_Member_PseudoConstant
::membershipStatus())) {
714 throw new Exception('Invalid Membership Status Id');
716 $values[$key] = $value;
719 case 'membership_status':
720 $membershipStatusId = CRM_Utils_Array
::key(ucfirst($value),
721 CRM_Member_PseudoConstant
::membershipStatus()
723 if ($membershipStatusId) {
724 if (!empty($values['status_id']) &&
725 $membershipStatusId != $values['status_id']
727 throw new Exception('Mismatched membership Status and Membership Status Id');
731 throw new Exception('Invalid Membership Status');
733 $values['status_id'] = $membershipStatusId;
741 _civicrm_api3_custom_format_params($params, $values, 'Membership');
745 // CRM_Member_BAO_Membership::create() handles membership_start_date,
746 // membership_end_date and membership_source. So, if $values contains
747 // membership_start_date, membership_end_date or membership_source,
748 // convert it to start_date, end_date or source
750 'membership_start_date' => 'start_date',
751 'membership_end_date' => 'end_date',
752 'membership_source' => 'source',
755 foreach ($changes as $orgVal => $changeVal) {
756 if (isset($values[$orgVal])) {
757 $values[$changeVal] = $values[$orgVal];
758 unset($values[$orgVal]);