From: Eileen McNaughton Date: Fri, 3 Jun 2022 23:27:54 +0000 (+1200) Subject: Membership form flow tidy-up X-Git-Url: https://vcs.fsf.org/?a=commitdiff_plain;h=22f90136080500c434832575bdc49a0d4fe11e34;p=civicrm-core.git Membership form flow tidy-up --- diff --git a/CRM/Contact/Import/Form/Summary.php b/CRM/Contact/Import/Form/Summary.php index c8adb53bfe..84b7c3b922 100644 --- a/CRM/Contact/Import/Form/Summary.php +++ b/CRM/Contact/Import/Form/Summary.php @@ -48,16 +48,7 @@ class CRM_Contact_Import_Form_Summary extends CRM_Import_Form_Summary { $this->assign('groupAdditions', $this->getUserJob()['metadata']['summary_info']['groups']); $this->assign('tagAdditions', $this->getUserJob()['metadata']['summary_info']['tags']); - $this->assign('totalRowCount', $this->getRowCount()); - $this->assign('validRowCount', $this->getRowCount(CRM_Import_Parser::VALID) + $this->getRowCount(CRM_Import_Parser::UNPARSED_ADDRESS_WARNING)); - $this->assign('invalidRowCount', $this->getRowCount(CRM_Import_Parser::ERROR)); - $this->assign('duplicateRowCount', $this->getRowCount(CRM_Import_Parser::DUPLICATE)); - $this->assign('unMatchCount', $this->getRowCount(CRM_Import_Parser::NO_MATCH)); - $this->assign('unparsedAddressCount', $this->getRowCount(CRM_Import_Parser::UNPARSED_ADDRESS_WARNING)); - $this->assign('downloadDuplicateRecordsUrl', $this->getDownloadURL(CRM_Import_Parser::DUPLICATE)); - $this->assign('downloadErrorRecordsUrl', $this->getDownloadURL(CRM_Import_Parser::ERROR)); - $this->assign('downloadMismatchRecordsUrl', $this->getDownloadURL(CRM_Import_Parser::NO_MATCH)); - $this->assign('downloadAddressRecordsUrl', $this->getDownloadURL(CRM_Import_Parser::UNPARSED_ADDRESS_WARNING)); + $this->assignOutputURLs(); $session = CRM_Core_Session::singleton(); $session->pushUserContext(CRM_Utils_System::url('civicrm/import/contact', 'reset=1')); } diff --git a/CRM/Member/Import/Form/MapField.php b/CRM/Member/Import/Form/MapField.php index 4b2e302683..5d2ba587d8 100644 --- a/CRM/Member/Import/Form/MapField.php +++ b/CRM/Member/Import/Form/MapField.php @@ -20,111 +20,21 @@ */ class CRM_Member_Import_Form_MapField extends CRM_Import_Form_MapField { - /** - * Set variables up before form is built. - * - * @return void - */ - public function preProcess() { - $this->_mapperFields = $this->get('fields'); - asort($this->_mapperFields); - parent::preProcess(); - - $skipColumnHeader = $this->controller->exportValue('DataSource', 'skipColumnHeader'); - $this->_onDuplicate = $this->get('onDuplicate', $onDuplicate ?? ""); - - $highlightedFields = []; - if ($this->getSubmittedValue('skipColumnHeader')) { - $this->assign('skipColumnHeader', $skipColumnHeader); - $this->assign('rowDisplayCount', 3); - } - else { - $this->assign('rowDisplayCount', 2); - } - - //CRM-2219 removing other required fields since for updation only - //membership id is required. - if ($this->getSubmittedValue('onDuplicate') == CRM_Import_Parser::DUPLICATE_UPDATE) { - $remove = array('membership_contact_id', 'email', 'first_name', 'last_name', 'external_identifier'); - foreach ($remove as $value) { - unset($this->_mapperFields[$value]); - } - $highlightedFieldsArray = array('membership_id', 'membership_start_date', 'membership_type_id'); - foreach ($highlightedFieldsArray as $name) { - $highlightedFields[] = $name; - } - } - elseif ($this->_onDuplicate == CRM_Import_Parser::DUPLICATE_SKIP) { - unset($this->_mapperFields['membership_id']); - $highlightedFieldsArray = array( - 'membership_contact_id', - 'email', - 'external_identifier', - 'membership_start_date', - 'membership_type_id', - ); - foreach ($highlightedFieldsArray as $name) { - $highlightedFields[] = $name; - } - } - - // modify field title - $this->_mapperFields['status_id'] = ts('Membership Status'); - $this->_mapperFields['membership_type_id'] = ts('Membership Type'); - $this->assign('highlightedFields', $highlightedFields); - } - /** * Build the form object. * * @return void */ public function buildQuickForm() { - //to save the current mappings - if (!$this->get('savedMapping')) { - $saveDetailsName = ts('Save this field mapping'); - $this->applyFilter('saveMappingName', 'trim'); - $this->add('text', 'saveMappingName', ts('Name')); - $this->add('text', 'saveMappingDesc', ts('Description')); - } - else { - $savedMapping = $this->get('savedMapping'); - - [$mappingName] = CRM_Core_BAO_Mapping::getMappingFields($savedMapping); - - $mappingName = $mappingName[1]; - - //mapping is to be loaded from database - - $this->set('loadedMapping', $savedMapping); - - $getMappingName = new CRM_Core_DAO_Mapping(); - $getMappingName->id = $savedMapping; - $getMappingName->mapping_type = 'Import Memberships'; - $getMappingName->find(); - while ($getMappingName->fetch()) { - $mapperName = $getMappingName->name; - } - - $this->assign('savedMappingName', $mapperName); - - $this->add('hidden', 'mappingId', $savedMapping); - - $this->addElement('checkbox', 'updateMapping', ts('Update this field mapping'), NULL); - $saveDetailsName = ts('Save as a new field mapping'); - $this->add('text', 'saveMappingName', ts('Name')); - $this->add('text', 'saveMappingDesc', ts('Description')); - } - - $this->addElement('checkbox', 'saveMapping', $saveDetailsName, NULL, array('onclick' => "showSaveDetails(this)")); - + $this->buildSavedMappingFields($this->getSubmittedValue('savedMapping')); $this->addFormRule(array('CRM_Member_Import_Form_MapField', 'formRule'), $this); //-------- end of saved mapping stuff --------- $defaults = []; $mapperKeys = array_keys($this->_mapperFields); - $hasHeaders = !empty($this->_columnHeaders); + $columnHeaders = $this->getColumnHeaders(); + $hasHeaders = $this->getSubmittedValue('skipColumnHeader'); $headerPatterns = $this->getHeaderPatterns(); $dataPatterns = $this->getDataPatterns(); @@ -135,7 +45,7 @@ class CRM_Member_Import_Form_MapField extends CRM_Import_Form_MapField { } $this->_location_types = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id'); $sel1 = $this->_mapperFields; - if (!$this->get('onDuplicate')) { + if (!$this->getSubmittedValue('onDuplicate')) { unset($sel1['id']); unset($sel1['membership_id']); } @@ -148,7 +58,7 @@ class CRM_Member_Import_Form_MapField extends CRM_Import_Form_MapField { //used to warn for mismatch column count or mismatch mapping $warning = 0; - for ($i = 0; $i < $this->_columnCount; $i++) { + foreach ($columnHeaders as $i => $columnHeader) { $sel = &$this->addElement('hierselect', "mapper[$i]", ts('Mapper for Field %1', array(1 => $i)), NULL); $jsSet = FALSE; if ($this->get('savedMapping')) { @@ -179,7 +89,7 @@ class CRM_Member_Import_Form_MapField extends CRM_Import_Form_MapField { $js .= "swapOptions($formName, 'mapper[$i]', 0, 3, 'hs_mapper_" . $i . "_');\n"; if ($hasHeaders) { - $defaults["mapper[$i]"] = array($this->defaultFromHeader($this->_columnHeaders[$i], $headerPatterns)); + $defaults["mapper[$i]"] = array($this->defaultFromHeader($columnHeader, $headerPatterns)); } else { $defaults["mapper[$i]"] = array($this->defaultFromData($dataPatterns, $i)); @@ -192,7 +102,7 @@ class CRM_Member_Import_Form_MapField extends CRM_Import_Form_MapField { if ($this->getSubmittedValue('skipColumnHeader')) { // Infer the default from the skipped headers if we have them $defaults["mapper[$i]"] = array( - $this->defaultFromHeader($this->_columnHeaders[$i], + $this->defaultFromHeader($columnHeader, $headerPatterns ), // $defaultLocationType->id @@ -277,23 +187,23 @@ class CRM_Member_Import_Form_MapField extends CRM_Import_Form_MapField { 'used' => 'Unsupervised', 'contact_type' => $self->getContactType(), ); - list($ruleFields, $threshold) = CRM_Dedupe_BAO_DedupeRuleGroup::dedupeRuleFieldsWeight($params); - $weightSum = 0; - foreach ($importKeys as $key => $val) { - if (array_key_exists($val, $ruleFields)) { - $weightSum += $ruleFields[$val]; - } - } - $fieldMessage = ''; - foreach ($ruleFields as $field => $weight) { - $fieldMessage .= ' ' . $field . '(weight ' . $weight . ')'; + [$ruleFields, $threshold] = CRM_Dedupe_BAO_DedupeRuleGroup::dedupeRuleFieldsWeight($params); + $weightSum = 0; + foreach ($importKeys as $key => $val) { + if (array_key_exists($val, $ruleFields)) { + $weightSum += $ruleFields[$val]; } + } + $fieldMessage = ''; + foreach ($ruleFields as $field => $weight) { + $fieldMessage .= ' ' . $field . '(weight ' . $weight . ')'; + } foreach ($requiredFields as $field => $title) { if (!in_array($field, $importKeys)) { if ($field == 'membership_contact_id') { if ((($weightSum >= $threshold || in_array('external_identifier', $importKeys)) && - $self->_onDuplicate != CRM_Import_Parser::DUPLICATE_UPDATE + $self->getSubmittedValue('onDuplicate') != CRM_Import_Parser::DUPLICATE_UPDATE ) || in_array('membership_id', $importKeys) ) { @@ -344,58 +254,12 @@ class CRM_Member_Import_Form_MapField extends CRM_Import_Form_MapField { } /** - * Process the mapped fields and map it into the uploaded file - * preview the file and extract some summary statistics + * Get the mapping name per the civicrm_mapping_field.type_id option group. * - * @return void + * @return string */ - public function postProcess() { - $params = $this->controller->exportValues('MapField'); - $this->updateUserJobMetadata('submitted_values', $this->getSubmittedValues()); - $mapper = []; - $mapperKeys = $this->controller->exportValue($this->_name, 'mapper'); - $mapperKeysMain = []; - - for ($i = 0; $i < $this->_columnCount; $i++) { - $mapper[$i] = $this->_mapperFields[$mapperKeys[$i][0]]; - $mapperKeysMain[$i] = $mapperKeys[$i][0]; - } - - $this->set('mapper', $mapper); - - // store mapping Id to display it in the preview page - if (!empty($params['mappingId'])) { - $this->set('loadMappingId', $params['mappingId']); - } - //Updating Mapping Records - if (!empty($params['updateMapping'])) { - for ($i = 0; $i < $this->_columnCount; $i++) { - $this->saveMappingField($params['mappingId'], $i, TRUE); - } - } - - //Saving Mapping Details and Records - if (!empty($params['saveMapping'])) { - $mappingParams = array( - 'name' => $params['saveMappingName'], - 'description' => $params['saveMappingDesc'], - 'mapping_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Mapping', 'mapping_type_id', 'Import Membership'), - ); - $saveMapping = CRM_Core_BAO_Mapping::add($mappingParams); - - for ($i = 0; $i < $this->_columnCount; $i++) { - $this->saveMappingField($saveMapping->id, $i); - } - $this->set('savedMapping', $saveMapping->id); - } - - $parser = new CRM_Member_Import_Parser_Membership($mapperKeysMain); - $parser->setUserJobID($this->getUserJobID()); - $parser->run($this->getSubmittedValue('uploadFile'), $this->getSubmittedValue('fieldSeparator'), $mapper, $this->getSubmittedValue('skipColumnHeader'), - CRM_Import_Parser::MODE_PREVIEW - ); - // add all the necessary variables to the form - $parser->set($this); + public function getMappingTypeName(): string { + return 'Import Membership'; } /** @@ -410,4 +274,50 @@ class CRM_Member_Import_Form_MapField extends CRM_Import_Form_MapField { return $this->parser; } + /** + * Get the fields to be highlighted in the UI. + * + * @return array + * @throws \CRM_Core_Exception + */ + protected function getHighlightedFields(): array { + $highlightedFields = []; + //CRM-2219 removing other required fields since for updation only + //membership id is required. + if ($this->getSubmittedValue('onDuplicate') == CRM_Import_Parser::DUPLICATE_UPDATE) { + $remove = [ + 'membership_contact_id', + 'email', + 'first_name', + 'last_name', + 'external_identifier', + ]; + foreach ($remove as $value) { + unset($this->_mapperFields[$value]); + } + $highlightedFieldsArray = [ + 'membership_id', + 'membership_start_date', + 'membership_type_id', + ]; + foreach ($highlightedFieldsArray as $name) { + $highlightedFields[] = $name; + } + } + elseif ($this->getSubmittedValue('onDuplicate') == CRM_Import_Parser::DUPLICATE_SKIP) { + unset($this->_mapperFields['membership_id']); + $highlightedFieldsArray = [ + 'membership_contact_id', + 'email', + 'external_identifier', + 'membership_start_date', + 'membership_type_id', + ]; + foreach ($highlightedFieldsArray as $name) { + $highlightedFields[] = $name; + } + } + return $highlightedFields; + } + } diff --git a/CRM/Member/Import/Form/Preview.php b/CRM/Member/Import/Form/Preview.php index 718a0b4337..9765b7ba8d 100644 --- a/CRM/Member/Import/Form/Preview.php +++ b/CRM/Member/Import/Form/Preview.php @@ -28,73 +28,9 @@ class CRM_Member_Import_Form_Preview extends CRM_Import_Form_Preview { */ public function preProcess() { parent::preProcess(); - $invalidRowCount = $this->get('invalidRowCount'); - if ($invalidRowCount) { - $urlParams = 'type=' . CRM_Import_Parser::ERROR . '&parser=CRM_Member_Import_Parser_Membership'; - $this->set('downloadErrorRecordsUrl', CRM_Utils_System::url('civicrm/export', $urlParams)); - } $this->setStatusUrl(); } - /** - * Process the mapped fields and map it into the uploaded file - * preview the file and extract some summary statistics - * - * @return void - */ - public function postProcess() { - $fileName = $this->getSubmittedValue('uploadFile'); - $invalidRowCount = $this->get('invalidRowCount'); - $onDuplicate = $this->get('onDuplicate'); - - $mapper = $this->controller->exportValue('MapField', 'mapper'); - - $parser = $this->getParser(); - - $mapFields = $this->get('fields'); - - foreach ($mapper as $key => $value) { - $header = []; - if (isset($mapFields[$mapper[$key][0]])) { - $header[] = $mapFields[$mapper[$key][0]]; - } - $mapperFields[] = implode(' - ', $header); - } - $parser->run($this->getSubmittedValue('uploadFile'), $this->getSubmittedValue('fieldSeparator'), - $mapperFields, - NULL, - CRM_Import_Parser::MODE_IMPORT, - NULL, - NULL, - $this->get('statusID') - ); - - // add all the necessary variables to the form - $parser->set($this, CRM_Import_Parser::MODE_IMPORT); - - // check if there is any error occurred - $errorStack = CRM_Core_Error::singleton(); - $errors = $errorStack->getErrors(); - $errorMessage = []; - - if (is_array($errors)) { - foreach ($errors as $key => $value) { - $errorMessage[] = $value['message']; - } - - $errorFile = $fileName['name'] . '.error.log'; - - if ($fd = fopen($errorFile, 'w')) { - fwrite($fd, implode('\n', $errorMessage)); - } - fclose($fd); - - $this->set('errorFile', $errorFile); - $urlParams = 'type=' . CRM_Import_Parser::ERROR . '&parser=CRM_Member_Import_Parser_Membership'; - $this->set('downloadErrorRecordsUrl', CRM_Utils_System::url('civicrm/export', $urlParams)); - } - } - /** * @return \CRM_Member_Import_Parser_Membership */ diff --git a/CRM/Member/Import/Form/Summary.php b/CRM/Member/Import/Form/Summary.php index 829b05c5fa..89e81e5722 100644 --- a/CRM/Member/Import/Form/Summary.php +++ b/CRM/Member/Import/Form/Summary.php @@ -20,66 +20,4 @@ */ class CRM_Member_Import_Form_Summary extends CRM_Import_Form_Summary { - /** - * Set variables up before form is built. - * - * @return void - */ - public function preProcess() { - // set the error message path to display - $this->assign('errorFile', $this->get('errorFile')); - - $totalRowCount = $this->get('totalRowCount'); - $this->set('totalRowCount', $totalRowCount); - - $invalidRowCount = $this->get('invalidRowCount'); - $duplicateRowCount = $this->get('duplicateRowCount'); - $onDuplicate = $this->get('onDuplicate'); - - if ($duplicateRowCount > 0) { - $urlParams = 'type=' . CRM_Import_Parser::DUPLICATE . '&parser=CRM_Member_Import_Parser_Membership'; - $this->set('downloadDuplicateRecordsUrl', CRM_Utils_System::url('civicrm/export', $urlParams)); - } - else { - $duplicateRowCount = 0; - $this->set('duplicateRowCount', $duplicateRowCount); - } - - $this->assign('dupeError', FALSE); - - if ($onDuplicate == CRM_Import_Parser::DUPLICATE_UPDATE) { - $dupeActionString = ts('These records have been updated with the imported data.'); - } - elseif ($onDuplicate == CRM_Import_Parser::DUPLICATE_FILL) { - $dupeActionString = ts('These records have been filled in with the imported data.'); - } - else { - /* Skip by default */ - - $dupeActionString = ts('These records have not been imported.'); - - $this->assign('dupeError', TRUE); - - /* only subtract dupes from successful import if we're skipping */ - - $this->set('validRowCount', $totalRowCount - $invalidRowCount - - $duplicateRowCount - ); - } - $this->assign('dupeActionString', $dupeActionString); - - $properties = [ - 'totalRowCount', - 'validRowCount', - 'invalidRowCount', - 'downloadErrorRecordsUrl', - 'duplicateRowCount', - 'downloadDuplicateRecordsUrl', - 'groupAdditions', - ]; - foreach ($properties as $property) { - $this->assign($property, $this->get($property)); - } - } - } diff --git a/CRM/Member/Import/Parser/Membership.php b/CRM/Member/Import/Parser/Membership.php index a000dc02b4..9ceda1d478 100644 --- a/CRM/Member/Import/Parser/Membership.php +++ b/CRM/Member/Import/Parser/Membership.php @@ -75,7 +75,6 @@ class CRM_Member_Import_Parser_Membership extends CRM_Import_Parser { * @param int $contactType * @param int $onDuplicate * @param int $statusID - * @param int $totalRowCount * * @return mixed * @throws Exception @@ -99,13 +98,6 @@ class CRM_Member_Import_Parser_Membership extends CRM_Import_Parser { $this->_errors = []; $this->_warnings = []; - - if ($mode == self::MODE_MAPFIELD) { - $this->_rows = []; - } - else { - $this->_activeFieldCount = count($this->_activeFields); - } if ($statusID) { $this->progressImport($statusID); $startTimestamp = $currTimestamp = $prevTimestamp = CRM_Utils_Time::time(); @@ -115,84 +107,12 @@ class CRM_Member_Import_Parser_Membership extends CRM_Import_Parser { $dataSource->setStatuses(['new']); while ($row = $dataSource->getRow()) { $values = array_values($row); - $this->_lineCount++; - - $this->_totalCount++; - - if ($mode == self::MODE_MAPFIELD) { - $returnCode = CRM_Import_Parser::VALID; - } - elseif ($mode == self::MODE_PREVIEW) { - $returnCode = $this->preview($values); - } - elseif ($mode == self::MODE_SUMMARY) { - $returnCode = $this->summary($values); - } - elseif ($mode == self::MODE_IMPORT) { - $returnCode = $this->import($this->getSubmittedValue('onDuplicate'), $values); + if ($mode == self::MODE_IMPORT) { + $this->import($this->getSubmittedValue('onDuplicate'), $values); if ($statusID && (($this->_lineCount % 50) == 0)) { $prevTimestamp = $this->progressImport($statusID, FALSE, $startTimestamp, $prevTimestamp, $totalRowCount); } } - else { - $returnCode = self::ERROR; - } - - // note that a line could be valid but still produce a warning - if ($returnCode & self::VALID) { - $this->_validCount++; - if ($mode == self::MODE_MAPFIELD) { - $this->_rows[] = $values; - $this->_activeFieldCount = max($this->_activeFieldCount, count($values)); - } - } - - if ($returnCode & self::ERROR) { - $this->_invalidRowCount++; - $recordNumber = $this->_lineCount; - array_unshift($values, $recordNumber); - $this->_errors[] = $values; - } - - if ($returnCode & self::DUPLICATE) { - $this->_duplicateCount++; - $recordNumber = $this->_lineCount; - array_unshift($values, $recordNumber); - $this->_duplicates[] = $values; - if ($this->getSubmittedValue('onDuplicate') != self::DUPLICATE_SKIP) { - $this->_validCount++; - } - } - } - - if ($mode == self::MODE_PREVIEW || $mode == self::MODE_IMPORT) { - $customHeaders = $mapper; - - $customfields = CRM_Core_BAO_CustomField::getFields('Membership'); - foreach ($customHeaders as $key => $value) { - if ($id = CRM_Core_BAO_CustomField::getKeyID($value)) { - $customHeaders[$key] = $customfields[$id][0]; - } - } - if ($this->_invalidRowCount) { - // removed view url for invlaid contacts - $headers = array_merge([ - ts('Line Number'), - ts('Reason'), - ], $customHeaders); - $this->_errorFileName = self::errorFileName(self::ERROR); - - self::exportCSV($this->_errorFileName, $headers, $this->_errors); - } - if ($this->_duplicateCount) { - $headers = array_merge([ - ts('Line Number'), - ts('View Membership URL'), - ], $customHeaders); - - $this->_duplicateFileName = self::errorFileName(self::DUPLICATE); - self::exportCSV($this->_duplicateFileName, $headers, $this->_duplicates); - } } } @@ -237,24 +157,6 @@ class CRM_Member_Import_Parser_Membership extends CRM_Import_Parser { return $params; } - /** - * Get the row from the csv mapped to our parameters. - * - * @param array $values - * - * @return array - * @throws \API_Exception - */ - public function getMappedRow(array $values): array { - $params = []; - foreach ($this->getFieldMappings() as $i => $mappedField) { - if ($mappedField['name']) { - $params[$this->getFieldMetadata($mappedField['name'])['name']] = $this->getTransformedFieldValue($mappedField['name'], $values[$i]); - } - } - return $params; - } - /** * @param string $name * @param $title @@ -292,33 +194,12 @@ class CRM_Member_Import_Parser_Membership extends CRM_Import_Parser { */ public function set($store, $mode = self::MODE_SUMMARY) { $store->set('lineCount', $this->_lineCount); - - $store->set('dataPatterns', $this->getDataPatterns()); - $store->set('columnCount', $this->_activeFieldCount); - - $store->set('totalRowCount', $this->_totalCount); $store->set('validRowCount', $this->_validCount); $store->set('invalidRowCount', $this->_invalidRowCount); - switch ($this->_contactType) { - case 'Individual': - $store->set('contactType', CRM_Import_Parser::CONTACT_INDIVIDUAL); - break; - - case 'Household': - $store->set('contactType', CRM_Import_Parser::CONTACT_HOUSEHOLD); - break; - - case 'Organization': - $store->set('contactType', CRM_Import_Parser::CONTACT_ORGANIZATION); - } - if ($this->_invalidRowCount) { $store->set('errorsFileName', $this->_errorFileName); } - if (isset($this->_rows) && !empty($this->_rows)) { - $store->set('dataValues', $this->_rows); - } if ($mode == self::MODE_IMPORT) { $store->set('duplicateRowCount', $this->_duplicateCount); @@ -381,20 +262,7 @@ class CRM_Member_Import_Parser_Membership extends CRM_Import_Parser { } /** - * Handle the values in preview mode. - * - * @param array $values - * The array of values belonging to this line. - * - * @return bool - * the result of this processing - */ - public function preview(&$values) { - return $this->summary($values); - } - - /** - * Handle the values in summary mode. + * Validate the values. * * @param array $values * The array of values belonging to this line. @@ -402,107 +270,24 @@ class CRM_Member_Import_Parser_Membership extends CRM_Import_Parser { * @return bool * the result of this processing */ - public function summary(&$values) { + public function validateValues($values) { $params = $this->getMappedRow($values); + $errors = []; + foreach ($params as $key => $value) { + $errors = array_merge($this->getInvalidValues($value, $key), $errors); + } if (empty($params['membership_type_id'])) { - array_unshift($values, ts('Missing required fields')); - return CRM_Import_Parser::ERROR; + $errors[] = ts('Missing required fields'); + return NULL; } - $errorMessage = NULL; //To check whether start date or join date is provided if (empty($params['start_date']) && empty($params['join_date'])) { - $errorMessage = 'Membership Start Date is required to create a memberships.'; - CRM_Contact_Import_Parser_Contact::addToErrorMsg('Start Date', $errorMessage); - } - - //for date-Formats - $session = CRM_Core_Session::singleton(); - $dateType = $session->get('dateTypes'); - foreach ($params as $key => $val) { - - if ($val) { - switch ($key) { - case 'join_date': - if (CRM_Utils_Date::convertToDefaultDate($params, $dateType, $key)) { - if (!CRM_Utils_Rule::date($params[$key])) { - CRM_Contact_Import_Parser_Contact::addToErrorMsg('Member Since', $errorMessage); - } - } - else { - CRM_Contact_Import_Parser_Contact::addToErrorMsg('Member Since', $errorMessage); - } - break; - - case 'start_date': - if (CRM_Utils_Date::convertToDefaultDate($params, $dateType, $key)) { - if (!CRM_Utils_Rule::date($params[$key])) { - CRM_Contact_Import_Parser_Contact::addToErrorMsg('Start Date', $errorMessage); - } - } - else { - CRM_Contact_Import_Parser_Contact::addToErrorMsg('Start Date', $errorMessage); - } - break; - - case 'end_date': - if (CRM_Utils_Date::convertToDefaultDate($params, $dateType, $key)) { - if (!CRM_Utils_Rule::date($params[$key])) { - CRM_Contact_Import_Parser_Contact::addToErrorMsg('End date', $errorMessage); - } - } - else { - CRM_Contact_Import_Parser_Contact::addToErrorMsg('End date', $errorMessage); - } - break; - - case 'status_override_end_date': - if (CRM_Utils_Date::convertToDefaultDate($params, $dateType, $key)) { - if (!CRM_Utils_Rule::date($params[$key])) { - CRM_Contact_Import_Parser_Contact::addToErrorMsg('Status Override End Date', $errorMessage); - } - } - else { - CRM_Contact_Import_Parser_Contact::addToErrorMsg('Status Override End Date', $errorMessage); - } - break; - - case 'membership_type_id': - // @todo - squish into membership status - can use same lines here too. - $membershipTypes = CRM_Member_PseudoConstant::membershipType(); - if (!CRM_Utils_Array::crmInArray($val, $membershipTypes) && - !array_key_exists($val, $membershipTypes) - ) { - CRM_Contact_Import_Parser_Contact::addToErrorMsg('Membership Type', $errorMessage); - } - break; - - case 'status_id': - if (!empty($val) && !$this->parsePseudoConstantField($val, $this->fieldMetadata[$key])) { - CRM_Contact_Import_Parser_Contact::addToErrorMsg('Membership Status', $errorMessage); - } - break; - - case 'email': - if (!CRM_Utils_Rule::email($val)) { - CRM_Contact_Import_Parser_Contact::addToErrorMsg('Email Address', $errorMessage); - } - } - } + $errors[] = 'Membership Start Date is required to create a memberships.'; } - //date-Format part ends - - $params['contact_type'] = 'Membership'; - - //checking error in custom data - $this->isErrorInCustomData($params, $errorMessage); - - if ($errorMessage) { - $tempMsg = "Invalid value for field(s) : $errorMessage"; - array_unshift($values, $tempMsg); - $errorMessage = NULL; - return CRM_Import_Parser::ERROR; + if ($errors) { + throw new CRM_Core_Exception('Invalid value for field(s) : ' . implode(',', $errors)); } return CRM_Import_Parser::VALID; @@ -520,12 +305,8 @@ class CRM_Member_Import_Parser_Membership extends CRM_Import_Parser { * the result of this processing */ public function import($onDuplicate, &$values) { + $rowNumber = (int) ($values[array_key_last($values)]); try { - // first make sure this is a valid line - $response = $this->summary($values); - if ($response != CRM_Import_Parser::VALID) { - return $response; - } $params = $this->getMappedRow($values); //assign join date equal to start date if join date is not provided @@ -589,6 +370,7 @@ class CRM_Member_Import_Parser_Membership extends CRM_Import_Parser { // onDuplicate == CRM_Import_Parser::DUPLICATE_UPDATE if (!empty($formatted['is_override']) && empty($formatted['status_id'])) { array_unshift($values, 'Required parameter missing: Status'); + $this->setImportStatus($rowNumber, 'ERROR', 'Required parameter missing: Status'); return CRM_Import_Parser::ERROR; } @@ -613,10 +395,12 @@ class CRM_Member_Import_Parser_Membership extends CRM_Import_Parser { $newMembership = civicrm_api3('Membership', 'create', $formatted); $this->_newMemberships[] = $newMembership['id']; + $this->setImportStatus($rowNumber, 'IMPORTED', 'Required parameter missing: Status'); return CRM_Import_Parser::VALID; } else { array_unshift($values, 'Matching Membership record not found for Membership ID ' . $formatValues['membership_id'] . '. Row was skipped.'); + $this->setImportStatus($rowNumber, 'ERROR', 'Matching Membership record not found for Membership ID ' . $formatValues['membership_id'] . '. Row was skipped.'); return CRM_Import_Parser::ERROR; } } @@ -634,6 +418,7 @@ class CRM_Member_Import_Parser_Membership extends CRM_Import_Parser { $matchedIDs = explode(',', $error['error_message']['params'][0]); if (count($matchedIDs) > 1) { array_unshift($values, 'Multiple matching contact records detected for this row. The membership was not imported'); + $this->setImportStatus($rowNumber, 'ERROR', 'Multiple matching contact records detected for this row. The membership was not imported'); return CRM_Import_Parser::ERROR; } else { @@ -668,12 +453,12 @@ class CRM_Member_Import_Parser_Membership extends CRM_Import_Parser { } elseif (empty($formatted['is_override'])) { if (empty($calcStatus)) { - 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.'); + $this->setImportStatus($rowNumber, 'ERROR', 'Status in import row (' . $formatValues['status_id'] . ') does not match calculated status based on your configured Membership Status Rules. Record was not imported.'); return CRM_Import_Parser::ERROR; } - elseif ($formatted['status_id'] != $calcStatus['id']) { + if ($formatted['status_id'] != $calcStatus['id']) { //Status Hold" is either NOT mapped or is FALSE - 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.'); + $this->setImportStatus($rowNumber, 'ERROR', '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.'); return CRM_Import_Parser::ERROR; } } @@ -681,6 +466,7 @@ class CRM_Member_Import_Parser_Membership extends CRM_Import_Parser { $newMembership = civicrm_api3('membership', 'create', $formatted); $this->_newMemberships[] = $newMembership['id']; + $this->setImportStatus($rowNumber, 'IMPORTED', ''); return CRM_Import_Parser::VALID; } } @@ -713,8 +499,7 @@ class CRM_Member_Import_Parser_Membership extends CRM_Import_Parser { $disp = $params['external_identifier']; } } - - array_unshift($values, 'No matching Contact found for (' . $disp . ')'); + $this->setImportStatus($rowNumber, 'ERROR', 'No matching Contact found for (' . $disp . ')'); return CRM_Import_Parser::ERROR; } } @@ -725,6 +510,7 @@ class CRM_Member_Import_Parser_Membership extends CRM_Import_Parser { $checkCid->find(TRUE); if ($checkCid->id != $formatted['contact_id']) { array_unshift($values, 'Mismatch of External ID:' . $formatValues['external_identifier'] . ' and Contact Id:' . $formatted['contact_id']); + $this->setImportStatus($rowNumber, 'ERROR', 'Mismatch of External ID:' . $formatValues['external_identifier'] . ' and Contact Id:' . $formatted['contact_id']); return CRM_Import_Parser::ERROR; } } @@ -758,11 +544,13 @@ class CRM_Member_Import_Parser_Membership extends CRM_Import_Parser { elseif (empty($formatted['is_override'])) { if (empty($calcStatus)) { 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.'); + $this->setImportStatus($rowNumber, 'ERROR', '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.'); return CRM_Import_Parser::ERROR; } elseif ($formatted['status_id'] != $calcStatus['id']) { //Status Hold" is either NOT mapped or is FALSE 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.'); + $this->setImportStatus($rowNumber, 'ERROR', '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.'); return CRM_Import_Parser::ERROR; } } @@ -770,11 +558,13 @@ class CRM_Member_Import_Parser_Membership extends CRM_Import_Parser { $newMembership = civicrm_api3('membership', 'create', $formatted); $this->_newMemberships[] = $newMembership['id']; + $this->setImportStatus($rowNumber, 'IMPORTED', ''); return CRM_Import_Parser::VALID; } } catch (Exception $e) { array_unshift($values, $e->getMessage()); + $this->setImportStatus($rowNumber, 'ERROR', $e->getMessage()); return CRM_Import_Parser::ERROR; } } @@ -902,4 +692,19 @@ class CRM_Member_Import_Parser_Membership extends CRM_Import_Parser { } } + /** + * Get the metadata field for which importable fields does not key the actual field name. + * + * @return string[] + */ + protected function getOddlyMappedMetadataFields(): array { + $uniqueNames = ['membership_id', 'membership_contact_id']; + $fields = []; + foreach ($uniqueNames as $name) { + $fields[$this->importableFieldsMetadata[$name]['name']] = $name; + } + // Include the parent fields as they could be present if required for matching ...in theory. + return array_merge($fields, parent::getOddlyMappedMetadataFields()); + } + } diff --git a/templates/CRM/Member/Import/Form/MapField.tpl b/templates/CRM/Member/Import/Form/MapField.tpl index 691755c7fb..287e40ba0d 100644 --- a/templates/CRM/Member/Import/Form/MapField.tpl +++ b/templates/CRM/Member/Import/Form/MapField.tpl @@ -19,7 +19,7 @@ {* @var $form Contains the array for the form elements and other form associated information assigned to the template by the controller *} {* Table for mapping data to CRM fields *} - {include file="CRM/Import/Form/MapTable.tpl"} + {include file="CRM/Import/Form/MapTableCommon.tpl" mapper=$form.mapper}
{include file="CRM/common/formButtons.tpl" location="bottom"}
diff --git a/templates/CRM/Member/Import/Form/Preview.tpl b/templates/CRM/Member/Import/Form/Preview.tpl index 809193341a..aa28d8b6c0 100644 --- a/templates/CRM/Member/Import/Form/Preview.tpl +++ b/templates/CRM/Member/Import/Form/Preview.tpl @@ -55,7 +55,7 @@ {* Table for mapping preview *} - {include file="CRM/Import/Form/MapTable.tpl"} + {include file="CRM/Import/Form/MapTableCommon.tpl"}
{include file="CRM/common/formButtons.tpl" location="bottom"}
diff --git a/tests/phpunit/CRM/Member/Import/Parser/MembershipTest.php b/tests/phpunit/CRM/Member/Import/Parser/MembershipTest.php index 62ad200bf2..91ae68307d 100644 --- a/tests/phpunit/CRM/Member/Import/Parser/MembershipTest.php +++ b/tests/phpunit/CRM/Member/Import/Parser/MembershipTest.php @@ -33,6 +33,11 @@ use Civi\Api4\UserJob; class CRM_Member_Import_Parser_MembershipTest extends CiviUnitTestCase { use CRMTraits_Custom_CustomDataTrait; + /** + * @var int + */ + protected $userJobID; + /** * Membership type name used in test function. * @@ -104,6 +109,9 @@ class CRM_Member_Import_Parser_MembershipTest extends CiviUnitTestCase { 'civicrm_membership_payment', 'civicrm_contact', 'civicrm_email', + 'civicrm_user_job', + 'civicrm_queue', + 'civicrm_queue_item', ]; $this->relationshipTypeDelete($this->_relationshipTypeId); $this->membershipTypeDelete(['id' => $this->_membershipTypeID]); @@ -382,4 +390,72 @@ class CRM_Member_Import_Parser_MembershipTest extends CiviUnitTestCase { $this->assertEquals('R', $membership[$this->getCustomFieldName('select_string')]); } + /** + * Test the full form-flow import. + */ + public function testImportCSV() :void { + $this->importCSV('memberships.csv', [ + ['name' => 'membership_contact_id'], + ['name' => 'membership_source'], + ['name' => 'membership_type_id'], + ['name' => 'membership_start_date'], + ['name' => 'do_not_import'], + ]); + $dataSource = new CRM_Import_DataSource_CSV($this->userJobID); + $row = $dataSource->getRow(); + $this->assertEquals('ERROR', $row['_status']); + $this->assertEquals('Invalid value for field(s) : Membership Type', $row['_status_message']); + } + + /** + * Import the csv file values. + * + * This function uses a flow that mimics the UI flow. + * + * @param string $csv Name of csv file. + * @param array $fieldMappings + * @param array $submittedValues + */ + protected function importCSV(string $csv, array $fieldMappings, array $submittedValues = []): void { + $submittedValues = array_merge([ + 'uploadFile' => ['name' => __DIR__ . '/data/' . $csv], + 'skipColumnHeader' => TRUE, + 'fieldSeparator' => ',', + 'contactType' => CRM_Import_Parser::CONTACT_INDIVIDUAL, + 'mapper' => $this->getMapperFromFieldMappings($fieldMappings), + 'dataSource' => 'CRM_Import_DataSource_CSV', + 'file' => ['name' => $csv], + 'dateFormats' => CRM_Core_Form_Date::DATE_yyyy_mm_dd, + 'onDuplicate' => CRM_Import_Parser::DUPLICATE_UPDATE, + 'groups' => [], + ], $submittedValues); + $form = $this->getFormObject('CRM_Member_Import_Form_DataSource', $submittedValues); + $form->buildForm(); + $form->postProcess(); + $this->userJobID = $form->getUserJobID(); + $form = $this->getFormObject('CRM_Member_Import_Form_MapField', $submittedValues); + $form->setUserJobID($this->userJobID); + $form->buildForm(); + $form->postProcess(); + /* @var CRM_Member_Import_Form_MapField $form */ + $form = $this->getFormObject('CRM_Member_Import_Form_Preview', $submittedValues); + $form->setUserJobID($this->userJobID); + $form->buildForm(); + $form->postProcess(); + } + + /** + * @param array $mappings + * + * @return array + */ + protected function getMapperFromFieldMappings(array $mappings): array { + $mapper = []; + foreach ($mappings as $mapping) { + $fieldInput = [$mapping['name']]; + $mapper[] = $fieldInput; + } + return $mapper; + } + } diff --git a/tests/phpunit/CiviTest/CiviUnitTestCase.php b/tests/phpunit/CiviTest/CiviUnitTestCase.php index 2bbbd7d67e..d0959aba7d 100644 --- a/tests/phpunit/CiviTest/CiviUnitTestCase.php +++ b/tests/phpunit/CiviTest/CiviUnitTestCase.php @@ -3243,6 +3243,18 @@ class CiviUnitTestCase extends PHPUnit\Framework\TestCase { $_SESSION['_' . $form->controller->_name . '_container']['values']['Preview'] = $formValues; return $form; + case 'CRM_Member_Import_Form_DataSource': + case 'CRM_Member_Import_Form_MapField': + case 'CRM_Member_Import_Form_Preview': + $form->controller = new CRM_Member_Import_Controller(); + $form->controller->setStateMachine(new CRM_Core_StateMachine($form->controller)); + // The submitted values should be set on one or the other of the forms in the flow. + // For test simplicity we set on all rather than figuring out which ones go where.... + $_SESSION['_' . $form->controller->_name . '_container']['values']['DataSource'] = $formValues; + $_SESSION['_' . $form->controller->_name . '_container']['values']['MapField'] = $formValues; + $_SESSION['_' . $form->controller->_name . '_container']['values']['Preview'] = $formValues; + return $form; + case strpos($class, '_Form_') !== FALSE: $form->controller = new CRM_Core_Controller_Simple($class, $pageName); break;