3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
19 * class to parse membership csv files
21 class CRM_Member_Import_Parser_Membership
extends CRM_Import_Parser
{
23 protected $_mapperKeys;
25 private $_membershipTypeIndex;
26 private $_membershipStatusIndex;
29 * Array of metadata for all available fields.
33 protected $fieldMetadata = [];
36 * Array of successfully imported membership id's
40 protected $_newMemberships;
52 * Separator being used
55 protected $_separator;
58 * Total number of lines in file
61 protected $_lineCount;
64 * Whether the file has a column header or not
68 protected $_haveColumnHeader;
75 public function __construct($mapperKeys = []) {
76 parent
::__construct();
77 $this->_mapperKeys
= $mapperKeys;
81 * @param string $fileName
82 * @param string $separator
84 * @param bool $skipColumnHeader
86 * @param int $contactType
87 * @param int $onDuplicate
88 * @param int $statusID
89 * @param int $totalRowCount
98 $skipColumnHeader = FALSE,
99 $mode = self
::MODE_PREVIEW
,
100 $contactType = self
::CONTACT_INDIVIDUAL
,
101 $onDuplicate = self
::DUPLICATE_SKIP
,
103 $totalRowCount = NULL
105 if (!is_array($fileName)) {
106 throw new CRM_Core_Exception('Unable to determine import file');
108 $fileName = $fileName['name'];
110 switch ($contactType) {
111 case self
::CONTACT_INDIVIDUAL
:
112 $this->_contactType
= 'Individual';
115 case self
::CONTACT_HOUSEHOLD
:
116 $this->_contactType
= 'Household';
119 case self
::CONTACT_ORGANIZATION
:
120 $this->_contactType
= 'Organization';
125 $this->_haveColumnHeader
= $skipColumnHeader;
127 $this->_separator
= $separator;
129 $fd = fopen($fileName, "r");
134 $this->_lineCount
= 0;
135 $this->_invalidRowCount
= $this->_validCount
= 0;
136 $this->_totalCount
= 0;
139 $this->_warnings
= [];
141 $this->_fileSize
= number_format(filesize($fileName) / 1024.0, 2);
143 if ($mode == self
::MODE_MAPFIELD
) {
147 $this->_activeFieldCount
= count($this->_activeFields
);
150 $this->progressImport($statusID);
151 $startTimestamp = $currTimestamp = $prevTimestamp = CRM_Utils_Time
::time();
157 $values = fgetcsv($fd, 8192, $separator);
162 self
::encloseScrub($values);
164 // skip column header if we're not in mapfield mode
165 if ($mode != self
::MODE_MAPFIELD
&& $skipColumnHeader) {
166 $skipColumnHeader = FALSE;
170 /* trim whitespace around the values */
172 foreach ($values as $k => $v) {
173 $values[$k] = trim($v, " \t\r\n");
175 if (CRM_Utils_System
::isNull($values)) {
179 $this->_totalCount++
;
181 if ($mode == self
::MODE_MAPFIELD
) {
182 $returnCode = CRM_Import_Parser
::VALID
;
184 elseif ($mode == self
::MODE_PREVIEW
) {
185 $returnCode = $this->preview($values);
187 elseif ($mode == self
::MODE_SUMMARY
) {
188 $returnCode = $this->summary($values);
190 elseif ($mode == self
::MODE_IMPORT
) {
191 $returnCode = $this->import($onDuplicate, $values);
192 if ($statusID && (($this->_lineCount %
50) == 0)) {
193 $prevTimestamp = $this->progressImport($statusID, FALSE, $startTimestamp, $prevTimestamp, $totalRowCount);
197 $returnCode = self
::ERROR
;
200 // note that a line could be valid but still produce a warning
201 if ($returnCode & self
::VALID
) {
202 $this->_validCount++
;
203 if ($mode == self
::MODE_MAPFIELD
) {
204 $this->_rows
[] = $values;
205 $this->_activeFieldCount
= max($this->_activeFieldCount
, count($values));
209 if ($returnCode & self
::ERROR
) {
210 $this->_invalidRowCount++
;
211 $recordNumber = $this->_lineCount
;
212 array_unshift($values, $recordNumber);
213 $this->_errors
[] = $values;
216 if ($returnCode & self
::DUPLICATE
) {
217 $this->_duplicateCount++
;
218 $recordNumber = $this->_lineCount
;
219 array_unshift($values, $recordNumber);
220 $this->_duplicates
[] = $values;
221 if ($onDuplicate != self
::DUPLICATE_SKIP
) {
222 $this->_validCount++
;
226 // if we are done processing the maxNumber of lines, break
227 if ($this->_maxLinesToProcess
> 0 && $this->_validCount
>= $this->_maxLinesToProcess
) {
234 if ($mode == self
::MODE_PREVIEW ||
$mode == self
::MODE_IMPORT
) {
235 $customHeaders = $mapper;
237 $customfields = CRM_Core_BAO_CustomField
::getFields('Membership');
238 foreach ($customHeaders as $key => $value) {
239 if ($id = CRM_Core_BAO_CustomField
::getKeyID($value)) {
240 $customHeaders[$key] = $customfields[$id][0];
243 if ($this->_invalidRowCount
) {
244 // removed view url for invlaid contacts
245 $headers = array_merge([
249 $this->_errorFileName
= self
::errorFileName(self
::ERROR
);
251 self
::exportCSV($this->_errorFileName
, $headers, $this->_errors
);
253 if ($this->_duplicateCount
) {
254 $headers = array_merge([
256 ts('View Membership URL'),
259 $this->_duplicateFileName
= self
::errorFileName(self
::DUPLICATE
);
260 self
::exportCSV($this->_duplicateFileName
, $headers, $this->_duplicates
);
266 * Given a list of the importable field keys that the user has selected
267 * set the active fields array to this list
269 * @param array $fieldKeys mapped array of values
273 public function setActiveFields($fieldKeys) {
274 $this->_activeFieldCount
= count($fieldKeys);
275 foreach ($fieldKeys as $key) {
276 if (empty($this->_fields
[$key])) {
277 $this->_activeFields
[] = new CRM_Member_Import_Field('', ts('- do not import -'));
280 $this->_activeFields
[] = clone($this->_fields
[$key]);
286 * Format the field values for input to the api.
289 * (reference ) associative array of name/value pairs
291 public function &getActiveFieldParams() {
293 for ($i = 0; $i < $this->_activeFieldCount
; $i++
) {
294 if (isset($this->_activeFields
[$i]->_value
)
295 && !isset($params[$this->_activeFields
[$i]->_name
])
296 && !isset($this->_activeFields
[$i]->_related
)
299 $params[$this->_activeFields
[$i]->_name
] = $this->_activeFields
[$i]->_value
;
306 * @param string $name
309 * @param string $headerPattern
310 * @param string $dataPattern
312 public function addField($name, $title, $type = CRM_Utils_Type
::T_INT
, $headerPattern = '//', $dataPattern = '//') {
314 $this->_fields
['doNotImport'] = new CRM_Member_Import_Field($name, $title, $type, $headerPattern, $dataPattern);
318 //$tempField = CRM_Contact_BAO_Contact::importableFields('Individual', null );
319 $tempField = CRM_Contact_BAO_Contact
::importableFields('All', NULL);
320 if (!array_key_exists($name, $tempField)) {
321 $this->_fields
[$name] = new CRM_Member_Import_Field($name, $title, $type, $headerPattern, $dataPattern);
324 $this->_fields
[$name] = new CRM_Contact_Import_Field($name, $title, $type, $headerPattern, $dataPattern,
325 CRM_Utils_Array
::value('hasLocationType', $tempField[$name])
332 * Store parser values.
334 * @param CRM_Core_Session $store
340 public function set($store, $mode = self
::MODE_SUMMARY
) {
341 $store->set('fileSize', $this->_fileSize
);
342 $store->set('lineCount', $this->_lineCount
);
343 $store->set('separator', $this->_separator
);
344 $store->set('fields', $this->getSelectValues());
346 $store->set('headerPatterns', $this->getHeaderPatterns());
347 $store->set('dataPatterns', $this->getDataPatterns());
348 $store->set('columnCount', $this->_activeFieldCount
);
350 $store->set('totalRowCount', $this->_totalCount
);
351 $store->set('validRowCount', $this->_validCount
);
352 $store->set('invalidRowCount', $this->_invalidRowCount
);
354 switch ($this->_contactType
) {
356 $store->set('contactType', CRM_Import_Parser
::CONTACT_INDIVIDUAL
);
360 $store->set('contactType', CRM_Import_Parser
::CONTACT_HOUSEHOLD
);
364 $store->set('contactType', CRM_Import_Parser
::CONTACT_ORGANIZATION
);
367 if ($this->_invalidRowCount
) {
368 $store->set('errorsFileName', $this->_errorFileName
);
370 if (isset($this->_rows
) && !empty($this->_rows
)) {
371 $store->set('dataValues', $this->_rows
);
374 if ($mode == self
::MODE_IMPORT
) {
375 $store->set('duplicateRowCount', $this->_duplicateCount
);
376 if ($this->_duplicateCount
) {
377 $store->set('duplicatesFileName', $this->_duplicateFileName
);
383 * Export data to a CSV file.
385 * @param string $fileName
386 * @param array $header
391 public static function exportCSV($fileName, $header, $data) {
393 $fd = fopen($fileName, 'w');
395 foreach ($header as $key => $value) {
396 $header[$key] = "\"$value\"";
398 $config = CRM_Core_Config
::singleton();
399 $output[] = implode($config->fieldSeparator
, $header);
401 foreach ($data as $datum) {
402 foreach ($datum as $key => $value) {
403 if (is_array($value)) {
404 foreach ($value[0] as $k1 => $v1) {
405 if ($k1 == 'location_type_id') {
412 $datum[$key] = "\"$value\"";
415 $output[] = implode($config->fieldSeparator
, $datum);
417 fwrite($fd, implode("\n", $output));
422 * The initializer code, called before the processing
426 public function init() {
427 $this->fieldMetadata
= CRM_Member_BAO_Membership
::importableFields($this->_contactType
, FALSE);
429 foreach ($this->fieldMetadata
as $name => $field) {
430 // @todo - we don't really need to do all this.... fieldMetadata is just fine to use as is.
431 $field['type'] = CRM_Utils_Array
::value('type', $field, CRM_Utils_Type
::T_INT
);
432 $field['dataPattern'] = CRM_Utils_Array
::value('dataPattern', $field, '//');
433 $field['headerPattern'] = CRM_Utils_Array
::value('headerPattern', $field, '//');
434 $this->addField($name, $field['title'], $field['type'], $field['headerPattern'], $field['dataPattern']);
437 $this->_newMemberships
= [];
439 $this->setActiveFields($this->_mapperKeys
);
441 // FIXME: we should do this in one place together with Form/MapField.php
442 $this->_membershipTypeIndex
= -1;
443 $this->_membershipStatusIndex
= -1;
446 foreach ($this->_mapperKeys
as $key) {
449 case 'membership_type_id':
450 $this->_membershipTypeIndex
= $index;
454 $this->_membershipStatusIndex
= $index;
462 * Handle the values in preview mode.
464 * @param array $values
465 * The array of values belonging to this line.
468 * the result of this processing
470 public function preview(&$values) {
471 return $this->summary($values);
475 * Handle the values in summary mode.
477 * @param array $values
478 * The array of values belonging to this line.
481 * the result of this processing
483 public function summary(&$values) {
485 $this->setActiveFieldValues($values);
487 $errorRequired = FALSE;
489 if ($this->_membershipTypeIndex
< 0) {
490 $errorRequired = TRUE;
493 $errorRequired = !CRM_Utils_Array
::value($this->_membershipTypeIndex
, $values);
496 if ($errorRequired) {
497 array_unshift($values, ts('Missing required fields'));
498 return CRM_Import_Parser
::ERROR
;
501 $params = $this->getActiveFieldParams();
502 $errorMessage = NULL;
504 //To check whether start date or join date is provided
505 if (empty($params['membership_start_date']) && empty($params['membership_join_date'])) {
506 $errorMessage = 'Membership Start Date is required to create a memberships.';
507 CRM_Contact_Import_Parser_Contact
::addToErrorMsg('Start Date', $errorMessage);
511 $session = CRM_Core_Session
::singleton();
512 $dateType = $session->get('dateTypes');
513 foreach ($params as $key => $val) {
517 case 'membership_join_date':
518 if (CRM_Utils_Date
::convertToDefaultDate($params, $dateType, $key)) {
519 if (!CRM_Utils_Rule
::date($params[$key])) {
520 CRM_Contact_Import_Parser_Contact
::addToErrorMsg('Member Since', $errorMessage);
524 CRM_Contact_Import_Parser_Contact
::addToErrorMsg('Member Since', $errorMessage);
528 case 'membership_start_date':
529 if (CRM_Utils_Date
::convertToDefaultDate($params, $dateType, $key)) {
530 if (!CRM_Utils_Rule
::date($params[$key])) {
531 CRM_Contact_Import_Parser_Contact
::addToErrorMsg('Start Date', $errorMessage);
535 CRM_Contact_Import_Parser_Contact
::addToErrorMsg('Start Date', $errorMessage);
539 case 'membership_end_date':
540 if (CRM_Utils_Date
::convertToDefaultDate($params, $dateType, $key)) {
541 if (!CRM_Utils_Rule
::date($params[$key])) {
542 CRM_Contact_Import_Parser_Contact
::addToErrorMsg('End date', $errorMessage);
546 CRM_Contact_Import_Parser_Contact
::addToErrorMsg('End date', $errorMessage);
550 case 'status_override_end_date':
551 if (CRM_Utils_Date
::convertToDefaultDate($params, $dateType, $key)) {
552 if (!CRM_Utils_Rule
::date($params[$key])) {
553 CRM_Contact_Import_Parser_Contact
::addToErrorMsg('Status Override End Date', $errorMessage);
557 CRM_Contact_Import_Parser_Contact
::addToErrorMsg('Status Override End Date', $errorMessage);
561 case 'membership_type_id':
562 // @todo - squish into membership status - can use same lines here too.
563 $membershipTypes = CRM_Member_PseudoConstant
::membershipType();
564 if (!CRM_Utils_Array
::crmInArray($val, $membershipTypes) &&
565 !array_key_exists($val, $membershipTypes)
567 CRM_Contact_Import_Parser_Contact
::addToErrorMsg('Membership Type', $errorMessage);
572 if (!empty($val) && !$this->parsePseudoConstantField($val, $this->fieldMetadata
[$key])) {
573 CRM_Contact_Import_Parser_Contact
::addToErrorMsg('Membership Status', $errorMessage);
578 if (!CRM_Utils_Rule
::email($val)) {
579 CRM_Contact_Import_Parser_Contact
::addToErrorMsg('Email Address', $errorMessage);
584 //date-Format part ends
586 $params['contact_type'] = 'Membership';
588 //checking error in custom data
589 CRM_Contact_Import_Parser_Contact
::isErrorInCustomData($params, $errorMessage);
592 $tempMsg = "Invalid value for field(s) : $errorMessage";
593 array_unshift($values, $tempMsg);
594 $errorMessage = NULL;
595 return CRM_Import_Parser
::ERROR
;
598 return CRM_Import_Parser
::VALID
;
602 * Handle the values in import mode.
604 * @param int $onDuplicate
605 * The code for what action to take on duplicates.
606 * @param array $values
607 * The array of values belonging to this line.
610 * the result of this processing
612 public function import($onDuplicate, &$values) {
614 // first make sure this is a valid line
615 $response = $this->summary($values);
616 if ($response != CRM_Import_Parser
::VALID
) {
620 $params = $this->getActiveFieldParams();
622 //assign join date equal to start date if join date is not provided
623 if (empty($params['membership_join_date']) && !empty($params['membership_start_date'])) {
624 $params['membership_join_date'] = $params['membership_start_date'];
627 $session = CRM_Core_Session
::singleton();
628 $dateType = CRM_Core_Session
::singleton()->get('dateTypes');
630 $customDataType = !empty($params['contact_type']) ?
$params['contact_type'] : 'Membership';
631 $customFields = CRM_Core_BAO_CustomField
::getFields($customDataType);
633 // don't add to recent items, CRM-4399
634 $formatted['skipRecentView'] = TRUE;
636 'membership_join_date' => ts('Member Since'),
637 'membership_start_date' => ts('Start Date'),
638 'membership_end_date' => ts('End Date'),
640 foreach ($params as $key => $val) {
643 case 'membership_join_date':
644 case 'membership_start_date':
645 case 'membership_end_date':
646 if (CRM_Utils_Date
::convertToDefaultDate($params, $dateType, $key)) {
647 if (!CRM_Utils_Rule
::date($params[$key])) {
648 CRM_Contact_Import_Parser_Contact
::addToErrorMsg($dateLabels[$key], $errorMessage);
652 CRM_Contact_Import_Parser_Contact
::addToErrorMsg($dateLabels[$key], $errorMessage);
656 case 'membership_type_id':
657 if (!is_numeric($val)) {
658 unset($params['membership_type_id']);
659 $params['membership_type'] = $val;
664 // @todo - we can do this based on the presence of 'pseudoconstant' in the metadata rather than field specific.
665 $params[$key] = $this->parsePseudoConstantField($val, $this->fieldMetadata
[$key]);
669 if ($customFieldID = CRM_Core_BAO_CustomField
::getKeyID($key)) {
670 if ($customFields[$customFieldID]['data_type'] == 'Date') {
671 CRM_Contact_Import_Parser_Contact
::formatCustomDate($params, $formatted, $dateType, $key);
672 unset($params[$key]);
674 elseif ($customFields[$customFieldID]['data_type'] == 'Boolean') {
675 $params[$key] = CRM_Utils_String
::strtoboolstr($val);
680 //date-Format part ends
683 foreach ($params as $key => $field) {
684 // ignore empty values or empty arrays etc
685 if (CRM_Utils_System
::isNull($field)) {
689 $formatValues[$key] = $field;
692 //format params to meet api v2 requirements.
693 //@todo find a way to test removing this formatting
694 $formatError = $this->membership_format_params($formatValues, $formatted, TRUE);
696 if ($onDuplicate != CRM_Import_Parser
::DUPLICATE_UPDATE
) {
697 $formatted['custom'] = CRM_Core_BAO_CustomField
::postProcess($formatted,
703 //fix for CRM-2219 Update Membership
704 // onDuplicate == CRM_Import_Parser::DUPLICATE_UPDATE
705 if (!empty($formatted['member_is_override']) && empty($formatted['status_id'])) {
706 array_unshift($values, 'Required parameter missing: Status');
707 return CRM_Import_Parser
::ERROR
;
710 if (!empty($formatValues['membership_id'])) {
711 $dao = new CRM_Member_BAO_Membership();
712 $dao->id
= $formatValues['membership_id'];
713 $dates = ['join_date', 'start_date', 'end_date'];
714 foreach ($dates as $v) {
715 if (empty($formatted[$v])) {
716 $formatted[$v] = CRM_Core_DAO
::getFieldValue('CRM_Member_DAO_Membership', $formatValues['membership_id'], $v);
720 $formatted['custom'] = CRM_Core_BAO_CustomField
::postProcess($formatted,
721 $formatValues['membership_id'],
724 if ($dao->find(TRUE)) {
725 if (empty($params['line_item']) && !empty($formatted['membership_type_id'])) {
726 CRM_Price_BAO_LineItem
::getLineItemArray($formatted, NULL, 'membership', $formatted['membership_type_id']);
729 $newMembership = civicrm_api3('Membership', 'create', $formatted);
730 $this->_newMemberships
[] = $newMembership['id'];
731 return CRM_Import_Parser
::VALID
;
734 array_unshift($values, 'Matching Membership record not found for Membership ID ' . $formatValues['membership_id'] . '. Row was skipped.');
735 return CRM_Import_Parser
::ERROR
;
741 $startDate = CRM_Utils_Date
::customFormat(CRM_Utils_Array
::value('start_date', $formatted), '%Y-%m-%d');
742 $endDate = CRM_Utils_Date
::customFormat(CRM_Utils_Array
::value('end_date', $formatted), '%Y-%m-%d');
743 $joinDate = CRM_Utils_Date
::customFormat(CRM_Utils_Array
::value('join_date', $formatted), '%Y-%m-%d');
745 if (!$this->isContactIDColumnPresent()) {
746 $error = $this->checkContactDuplicate($formatValues);
748 if (CRM_Core_Error
::isAPIError($error, CRM_Core_ERROR
::DUPLICATE_CONTACT
)) {
749 $matchedIDs = explode(',', $error['error_message']['params'][0]);
750 if (count($matchedIDs) > 1) {
751 array_unshift($values, 'Multiple matching contact records detected for this row. The membership was not imported');
752 return CRM_Import_Parser
::ERROR
;
755 $cid = $matchedIDs[0];
756 $formatted['contact_id'] = $cid;
759 $calcDates = CRM_Member_BAO_MembershipType
::getDatesForMembershipType($formatted['membership_type_id'],
764 self
::formattedDates($calcDates, $formatted);
766 //fix for CRM-3570, exclude the statuses those having is_admin = 1
767 //now user can import is_admin if is override is true.
768 $excludeIsAdmin = FALSE;
769 if (empty($formatted['member_is_override'])) {
770 $formatted['exclude_is_admin'] = $excludeIsAdmin = TRUE;
772 $calcStatus = CRM_Member_BAO_MembershipStatus
::getMembershipStatusByDate($startDate,
777 $formatted['membership_type_id'],
781 if (empty($formatted['status_id'])) {
782 $formatted['status_id'] = $calcStatus['id'];
784 elseif (empty($formatted['member_is_override'])) {
785 if (empty($calcStatus)) {
786 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.');
787 return CRM_Import_Parser
::ERROR
;
789 elseif ($formatted['status_id'] != $calcStatus['id']) {
790 //Status Hold" is either NOT mapped or is FALSE
791 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.');
792 return CRM_Import_Parser
::ERROR
;
796 $newMembership = civicrm_api3('membership', 'create', $formatted);
798 $this->_newMemberships
[] = $newMembership['id'];
799 return CRM_Import_Parser
::VALID
;
803 // Using new Dedupe rule.
805 'contact_type' => $this->_contactType
,
806 'used' => 'Unsupervised',
808 $fieldsArray = CRM_Dedupe_BAO_DedupeRule
::dedupeRuleFields($ruleParams);
811 foreach ($fieldsArray as $value) {
812 if (array_key_exists(trim($value), $params)) {
813 $paramValue = $params[trim($value)];
814 if (is_array($paramValue)) {
815 $disp .= $params[trim($value)][0][trim($value)] . " ";
818 $disp .= $params[trim($value)] . " ";
823 if (!empty($params['external_identifier'])) {
825 $disp .= "AND {$params['external_identifier']}";
828 $disp = $params['external_identifier'];
832 array_unshift($values, 'No matching Contact found for (' . $disp . ')');
833 return CRM_Import_Parser
::ERROR
;
837 if (!empty($formatValues['external_identifier'])) {
838 $checkCid = new CRM_Contact_DAO_Contact();
839 $checkCid->external_identifier
= $formatValues['external_identifier'];
840 $checkCid->find(TRUE);
841 if ($checkCid->id
!= $formatted['contact_id']) {
842 array_unshift($values, 'Mismatch of External ID:' . $formatValues['external_identifier'] . ' and Contact Id:' . $formatted['contact_id']);
843 return CRM_Import_Parser
::ERROR
;
848 $calcDates = CRM_Member_BAO_MembershipType
::getDatesForMembershipType($formatted['membership_type_id'],
853 self
::formattedDates($calcDates, $formatted);
854 //end of date calculation part
856 //fix for CRM-3570, exclude the statuses those having is_admin = 1
857 //now user can import is_admin if is override is true.
858 $excludeIsAdmin = FALSE;
859 if (empty($formatted['member_is_override'])) {
860 $formatted['exclude_is_admin'] = $excludeIsAdmin = TRUE;
862 $calcStatus = CRM_Member_BAO_MembershipStatus
::getMembershipStatusByDate($startDate,
867 $formatted['membership_type_id'],
870 if (empty($formatted['status_id'])) {
871 $formatted['status_id'] = $calcStatus['id'] ??
NULL;
873 elseif (empty($formatted['member_is_override'])) {
874 if (empty($calcStatus)) {
875 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.');
876 return CRM_Import_Parser
::ERROR
;
878 elseif ($formatted['status_id'] != $calcStatus['id']) {
879 //Status Hold" is either NOT mapped or is FALSE
880 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.');
881 return CRM_Import_Parser
::ERROR
;
885 $newMembership = civicrm_api3('membership', 'create', $formatted);
887 $this->_newMemberships
[] = $newMembership['id'];
888 return CRM_Import_Parser
::VALID
;
891 catch (Exception
$e) {
892 array_unshift($values, $e->getMessage());
893 return CRM_Import_Parser
::ERROR
;
898 * Get the array of successfully imported membership id's
902 public function &getImportedMemberships() {
903 return $this->_newMemberships
;
907 * to calculate join, start and end dates
909 * @param array $calcDates
910 * Array of dates returned by getDatesForMembershipType().
915 public function formattedDates($calcDates, &$formatted) {
922 foreach ($dates as $d) {
923 if (isset($formatted[$d]) &&
924 !CRM_Utils_System
::isNull($formatted[$d])
926 $formatted[$d] = CRM_Utils_Date
::isoToMysql($formatted[$d]);
928 elseif (isset($calcDates[$d])) {
929 $formatted[$d] = CRM_Utils_Date
::isoToMysql($calcDates[$d]);
935 * @deprecated - this function formats params according to v2 standards but
936 * need to be sure about the impact of not calling it so retaining on the import class
937 * take the input parameter list as specified in the data model and
938 * convert it into the same format that we use in QF and BAO object
940 * @param array $params
941 * Associative array of property name/value.
942 * pairs to insert in new contact.
943 * @param array $values
944 * The reformatted properties that we can use internally.
946 * @param array|bool $create Is the formatted Values array going to
947 * be used for CRM_Member_BAO_Membership:create()
950 * @return array|error
952 public function membership_format_params($params, &$values, $create = FALSE) {
953 require_once 'api/v3/utils.php';
954 $fields = CRM_Member_DAO_Membership
::fields();
955 _civicrm_api3_store_values($fields, $params, $values);
957 $customFields = CRM_Core_BAO_CustomField
::getFields('Membership');
959 foreach ($params as $key => $value) {
961 //Handling Custom Data
962 if ($customFieldID = CRM_Core_BAO_CustomField
::getKeyID($key)) {
963 $values[$key] = $value;
964 $type = $customFields[$customFieldID]['html_type'];
965 if (CRM_Core_BAO_CustomField
::isSerialized($customFields[$customFieldID])) {
966 $values[$key] = self
::unserializeCustomValue($customFieldID, $value, $type);
971 case 'membership_contact_id':
972 if (!CRM_Utils_Rule
::integer($value)) {
973 throw new Exception("contact_id not valid: $value");
975 $dao = new CRM_Core_DAO();
977 $svq = $dao->singleValueQuery("SELECT id FROM civicrm_contact WHERE id = $value",
981 throw new Exception("Invalid Contact ID: There is no contact record with contact_id = $value.");
983 $values['contact_id'] = $values['membership_contact_id'];
984 unset($values['membership_contact_id']);
987 case 'membership_type_id':
988 if (!array_key_exists($value, CRM_Member_PseudoConstant
::membershipType())) {
989 throw new Exception('Invalid Membership Type Id');
991 $values[$key] = $value;
994 case 'membership_type':
995 $membershipTypeId = CRM_Utils_Array
::key(ucfirst($value),
996 CRM_Member_PseudoConstant
::membershipType()
998 if ($membershipTypeId) {
999 if (!empty($values['membership_type_id']) &&
1000 $membershipTypeId != $values['membership_type_id']
1002 throw new Exception('Mismatched membership Type and Membership Type Id');
1006 throw new Exception('Invalid Membership Type');
1008 $values['membership_type_id'] = $membershipTypeId;
1017 // CRM_Member_BAO_Membership::create() handles membership_start_date, membership_join_date,
1018 // membership_end_date and membership_source. So, if $values contains
1019 // membership_start_date, membership_end_date, membership_join_date or membership_source,
1020 // convert it to start_date, end_date, join_date or source
1022 'membership_join_date' => 'join_date',
1023 'membership_start_date' => 'start_date',
1024 'membership_end_date' => 'end_date',
1025 'membership_source' => 'source',
1028 foreach ($changes as $orgVal => $changeVal) {
1029 if (isset($values[$orgVal])) {
1030 $values[$changeVal] = $values[$orgVal];
1031 unset($values[$orgVal]);
1040 * Is the contact ID mapped.
1044 protected function isContactIDColumnPresent(): bool {
1045 return in_array('membership_contact_id', $this->_mapperKeys
, TRUE);