Merge pull request #23700 from eileenmcnaughton/import_cont_err
[civicrm-core.git] / CRM / Member / Import / Form / MapField.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
bc77d7c0
TO
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 |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035
TO
11
12/**
13 *
14 * @package CRM
ca5cec67 15 * @copyright CiviCRM LLC https://civicrm.org/licensing
6a488035
TO
16 */
17
18/**
19 * This class gets the name of the file to upload
20 */
b26295b8 21class CRM_Member_Import_Form_MapField extends CRM_Import_Form_MapField {
6a488035 22
6a488035 23 /**
fe482240 24 * Build the form object.
6a488035
TO
25 *
26 * @return void
6a488035
TO
27 */
28 public function buildQuickForm() {
22f90136 29 $this->buildSavedMappingFields($this->getSubmittedValue('savedMapping'));
6a488035
TO
30 $this->addFormRule(array('CRM_Member_Import_Form_MapField', 'formRule'), $this);
31
32 //-------- end of saved mapping stuff ---------
33
affcc9d2 34 $defaults = [];
22f90136
EM
35 $columnHeaders = $this->getColumnHeaders();
36 $hasHeaders = $this->getSubmittedValue('skipColumnHeader');
b584b608
EM
37 $headerPatterns = $this->getHeaderPatterns();
38 $dataPatterns = $this->getDataPatterns();
2238da0d
EM
39 // For most fields using the html label is a good thing
40 // but for contact ID we really want to specify ID.
41 $this->_mapperFields['membership_contact_id'] = ts('Contact ID');
6a488035 42 $sel1 = $this->_mapperFields;
22f90136 43 if (!$this->getSubmittedValue('onDuplicate')) {
2238da0d 44 // If not updating then do not allow membership id.
6a488035
TO
45 unset($sel1['membership_id']);
46 }
47
48 $sel2[''] = NULL;
49
50 $js = "<script type='text/javascript'>\n";
51 $formName = 'document.forms.' . $this->_name;
52
53 //used to warn for mismatch column count or mismatch mapping
54 $warning = 0;
2238da0d
EM
55 $savedMappingID = $this->getSubmittedValue('savedMapping');
56 if ($savedMappingID) {
57 $fieldMappings = \Civi\Api4\MappingField::get(FALSE)->addWhere('mapping_id', '=', $savedMappingID)->execute()->indexBy('column_number');
58 }
6a488035 59
22f90136 60 foreach ($columnHeaders as $i => $columnHeader) {
6a488035
TO
61 $sel = &$this->addElement('hierselect', "mapper[$i]", ts('Mapper for Field %1', array(1 => $i)), NULL);
62 $jsSet = FALSE;
af05126d 63 if ($this->getSubmittedValue('savedMapping')) {
2238da0d
EM
64 $fieldMapping = $fieldMappings[$i] ?? NULL;
65 if (isset($fieldMappings[$i])) {
66 if ($fieldMapping['name'] != ts('do_not_import')) {
6a488035
TO
67 //When locationType is not set
68 $js .= "{$formName}['mapper[$i][1]'].style.display = 'none';\n";
69
70 //When phoneType is not set
71 $js .= "{$formName}['mapper[$i][2]'].style.display = 'none';\n";
72
73 $js .= "{$formName}['mapper[$i][3]'].style.display = 'none';\n";
74
2238da0d 75 $defaults["mapper[$i]"] = [$fieldMapping['name']];
6a488035
TO
76 $jsSet = TRUE;
77 }
78 else {
affcc9d2 79 $defaults["mapper[$i]"] = [];
6a488035
TO
80 }
81 if (!$jsSet) {
82 for ($k = 1; $k < 4; $k++) {
83 $js .= "{$formName}['mapper[$i][$k]'].style.display = 'none';\n";
84 }
85 }
86 }
87 else {
88 // this load section to help mapping if we ran out of saved columns when doing Load Mapping
89 $js .= "swapOptions($formName, 'mapper[$i]', 0, 3, 'hs_mapper_" . $i . "_');\n";
90
91 if ($hasHeaders) {
22f90136 92 $defaults["mapper[$i]"] = array($this->defaultFromHeader($columnHeader, $headerPatterns));
6a488035
TO
93 }
94 else {
95 $defaults["mapper[$i]"] = array($this->defaultFromData($dataPatterns, $i));
96 }
97 }
98 //end of load mapping
99 }
100 else {
101 $js .= "swapOptions($formName, 'mapper[$i]', 0, 3, 'hs_mapper_" . $i . "_');\n";
209cedd9 102 if ($this->getSubmittedValue('skipColumnHeader')) {
6a488035
TO
103 // Infer the default from the skipped headers if we have them
104 $defaults["mapper[$i]"] = array(
22f90136 105 $this->defaultFromHeader($columnHeader,
6a488035
TO
106 $headerPatterns
107 ),
108 // $defaultLocationType->id
109 0,
110 );
111 }
112 else {
113 // Otherwise guess the default from the form of the data
114 $defaults["mapper[$i]"] = array(
115 $this->defaultFromData($dataPatterns, $i),
116 // $defaultLocationType->id
117 0,
118 );
119 }
120 }
2238da0d 121 $sel->setOptions(array($sel1, $sel2));
6a488035
TO
122 }
123 $js .= "</script>\n";
124 $this->assign('initHideBoxes', $js);
125
126 //set warning if mismatch in more than
127 if (isset($mappingName)) {
128 if (($this->_columnCount != count($mappingName))) {
129 $warning++;
130 }
131 }
132 if ($warning != 0 && $this->get('savedMapping')) {
133 $session = CRM_Core_Session::singleton();
2238da0d 134 $session::setStatus(ts('The data columns in this import file appear to be different from the saved mapping. Please verify that you have selected the correct saved mapping before continuing.'));
6a488035
TO
135 }
136 else {
137 $session = CRM_Core_Session::singleton();
2238da0d 138 $session::setStatus(NULL);
6a488035
TO
139 }
140
141 $this->setDefaults($defaults);
142
143 $this->addButtons(array(
c5c263ca
AH
144 array(
145 'type' => 'back',
146 'name' => ts('Previous'),
147 ),
148 array(
149 'type' => 'next',
150 'name' => ts('Continue'),
151 'spacing' => '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;',
152 'isDefault' => TRUE,
153 ),
154 array(
155 'type' => 'cancel',
156 'name' => ts('Cancel'),
157 ),
158 ));
6a488035
TO
159 }
160
161 /**
fe482240 162 * Global validation rules for the form.
6a488035 163 *
b2363ea8
TO
164 * @param array $fields
165 * Posted values of the form.
6a488035 166 *
fd31fa4c 167 * @param $files
e8cf95b4 168 * @param self $self
fd31fa4c 169 *
2238da0d 170 * @return array|bool
a6c01b45 171 * list of errors to be posted back to the form
6a488035 172 */
00be9182 173 public static function formRule($fields, $files, $self) {
affcc9d2 174 $errors = [];
6a488035 175
90ea7810
EM
176 $importKeys = [];
177 foreach ($fields['mapper'] as $mapperPart) {
178 $importKeys[] = $mapperPart[0];
179 }
180 // FIXME: should use the schema titles, not redeclare them
181 $requiredFields = array(
182 'membership_contact_id' => ts('Contact ID'),
183 'membership_type_id' => ts('Membership Type'),
184 'membership_start_date' => ts('Membership Start Date'),
185 );
b584b608
EM
186 $params = array(
187 'used' => 'Unsupervised',
188 'contact_type' => $self->getContactType(),
189 );
22f90136
EM
190 [$ruleFields, $threshold] = CRM_Dedupe_BAO_DedupeRuleGroup::dedupeRuleFieldsWeight($params);
191 $weightSum = 0;
192 foreach ($importKeys as $key => $val) {
193 if (array_key_exists($val, $ruleFields)) {
194 $weightSum += $ruleFields[$val];
6a488035 195 }
22f90136
EM
196 }
197 $fieldMessage = '';
198 foreach ($ruleFields as $field => $weight) {
199 $fieldMessage .= ' ' . $field . '(weight ' . $weight . ')';
200 }
6a488035 201
90ea7810
EM
202 foreach ($requiredFields as $field => $title) {
203 if (!in_array($field, $importKeys)) {
2238da0d 204 if ($field === 'membership_contact_id') {
90ea7810 205 if ((($weightSum >= $threshold || in_array('external_identifier', $importKeys)) &&
22f90136 206 $self->getSubmittedValue('onDuplicate') != CRM_Import_Parser::DUPLICATE_UPDATE
90ea7810
EM
207 ) ||
208 in_array('membership_id', $importKeys)
209 ) {
210 continue;
6a488035 211 }
2238da0d
EM
212 if (!isset($errors['_qf_default'])) {
213 $errors['_qf_default'] = '';
6a488035 214 }
2238da0d
EM
215 $errors['_qf_default'] .= ts('Missing required contact matching fields.') . " $fieldMessage " . ts('(Sum of all weights should be greater than or equal to threshold: %1).', array(
216 1 => $threshold,
217 )) . ' ' . ts('(OR Membership ID if update mode.)') . '<br />';
6a488035 218 }
90ea7810
EM
219 else {
220 if (!isset($errors['_qf_default'])) {
221 $errors['_qf_default'] = '';
222 }
223 $errors['_qf_default'] .= ts('Missing required field: %1', array(
224 1 => $title,
225 )) . '<br />';
226 }
6a488035
TO
227 }
228 }
229
a7488080 230 if (!empty($fields['saveMapping'])) {
9c1bc317 231 $nameField = $fields['saveMappingName'] ?? NULL;
6a488035
TO
232 if (empty($nameField)) {
233 $errors['saveMappingName'] = ts('Name is required to save Import Mapping');
234 }
235 else {
95f52e3b 236 if (CRM_Core_BAO_Mapping::checkMapping($nameField, CRM_Core_PseudoConstant::getKey('CRM_Core_BAO_Mapping', 'mapping_type_id', 'Import Membership'))) {
6a488035
TO
237 $errors['saveMappingName'] = ts('Duplicate Import Membership Mapping Name');
238 }
239 }
240 }
241
242 if (!empty($errors)) {
243 if (!empty($errors['saveMappingName'])) {
244 $_flag = 1;
245 $assignError = new CRM_Core_Page();
246 $assignError->assign('mappingDetailsError', $_flag);
247 }
248 return $errors;
249 }
250
251 return TRUE;
252 }
253
254 /**
22f90136 255 * Get the mapping name per the civicrm_mapping_field.type_id option group.
6a488035 256 *
22f90136 257 * @return string
6a488035 258 */
22f90136
EM
259 public function getMappingTypeName(): string {
260 return 'Import Membership';
6a488035 261 }
96025800 262
83a1c234
EM
263 /**
264 * @return \CRM_Member_Import_Parser_Membership
265 */
266 protected function getParser(): CRM_Member_Import_Parser_Membership {
267 if (!$this->parser) {
268 $this->parser = new CRM_Member_Import_Parser_Membership();
269 $this->parser->setUserJobID($this->getUserJobID());
270 $this->parser->init();
271 }
272 return $this->parser;
273 }
274
22f90136
EM
275 /**
276 * Get the fields to be highlighted in the UI.
277 *
278 * @return array
279 * @throws \CRM_Core_Exception
280 */
281 protected function getHighlightedFields(): array {
282 $highlightedFields = [];
2238da0d 283 //CRM-2219 removing other required fields since for update only
22f90136
EM
284 //membership id is required.
285 if ($this->getSubmittedValue('onDuplicate') == CRM_Import_Parser::DUPLICATE_UPDATE) {
286 $remove = [
287 'membership_contact_id',
288 'email',
289 'first_name',
290 'last_name',
291 'external_identifier',
292 ];
293 foreach ($remove as $value) {
294 unset($this->_mapperFields[$value]);
295 }
296 $highlightedFieldsArray = [
297 'membership_id',
298 'membership_start_date',
299 'membership_type_id',
300 ];
301 foreach ($highlightedFieldsArray as $name) {
302 $highlightedFields[] = $name;
303 }
304 }
305 elseif ($this->getSubmittedValue('onDuplicate') == CRM_Import_Parser::DUPLICATE_SKIP) {
306 unset($this->_mapperFields['membership_id']);
307 $highlightedFieldsArray = [
308 'membership_contact_id',
309 'email',
310 'external_identifier',
311 'membership_start_date',
312 'membership_type_id',
313 ];
314 foreach ($highlightedFieldsArray as $name) {
315 $highlightedFields[] = $name;
316 }
317 }
318 return $highlightedFields;
319 }
320
6a488035 321}