Membership form flow tidy-up
authorEileen McNaughton <emcnaughton@wikimedia.org>
Fri, 3 Jun 2022 23:27:54 +0000 (11:27 +1200)
committerEileen McNaughton <emcnaughton@wikimedia.org>
Mon, 6 Jun 2022 04:46:38 +0000 (16:46 +1200)
CRM/Contact/Import/Form/Summary.php
CRM/Member/Import/Form/MapField.php
CRM/Member/Import/Form/Preview.php
CRM/Member/Import/Form/Summary.php
CRM/Member/Import/Parser/Membership.php
templates/CRM/Member/Import/Form/MapField.tpl
templates/CRM/Member/Import/Form/Preview.tpl
tests/phpunit/CRM/Member/Import/Parser/MembershipTest.php
tests/phpunit/CiviTest/CiviUnitTestCase.php

index c8adb53bfe50feb3a0587e223852028f6aaf8dcb..84b7c3b922f0a7c40ccd347adb611e476b02a19e 100644 (file)
@@ -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'));
   }
index 4b2e302683996a6f07566677fa216ca20d753eba..5d2ba587d832aafdd7b2e88f64b5408889869cc7 100644 (file)
  */
 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;
+  }
+
 }
index 718a0b4337f378e4d5d2e83c02184673ee4a8f13..9765b7ba8d61d16801f88da2c2e0e45fe0534ad7 100644 (file)
@@ -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
    */
index 829b05c5fa6c0c47817940cf62170f38c348be63..89e81e57228769aa435e8c00db76b6019100e7c4 100644 (file)
  */
 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));
-    }
-  }
-
 }
index a000dc02b48da88d8feeca7bbd915e31a3cdf08b..9ceda1d478648ad64780d7469b4970290a2677c6 100644 (file)
@@ -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());
+  }
+
 }
index 691755c7fb102179e5f1ab5104cc7ba66369b8c4..287e40ba0dfe095f5308ab3b50c1ca998ef5254e 100644 (file)
@@ -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}
 
 <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location="bottom"}</div>
 </div>
index 809193341af134a85a1e1a99d094e16e0a3deab5..aa28d8b6c03148189d30dd8ccb0473bcd37a075c 100644 (file)
@@ -55,7 +55,7 @@
 
 
  {* Table for mapping preview *}
- {include file="CRM/Import/Form/MapTable.tpl"}
+ {include file="CRM/Import/Form/MapTableCommon.tpl"}
 
 
  <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location="bottom"}</div>
index 62ad200bf283cb925e6d24e14cc8eeb4eaf66421..91ae68307dc9a11b870c5f5fb9a3a69ba636729f 100644 (file)
@@ -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;
+  }
+
 }
index 2bbbd7d67e66cce0241f153e9bef588c30d1bc9a..d0959aba7d6ecb095c28b4db699a0a1bcd0dea29 100644 (file)
@@ -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;