Merge pull request #22826 from colemanw/api4EntityTypes
[civicrm-core.git] / tests / phpunit / CRM / Case / Form / CustomDataTest.php
1 <?php
2 require_once 'CiviTest/CiviCaseTestCase.php';
3
4 /**
5 * Class CRM_Case_Form_CustomDataTest
6 * @group headless
7 */
8 class CRM_Case_Form_CustomDataTest extends CiviCaseTestCase {
9
10 protected $custom_group;
11
12 public function setUp(): void {
13 parent::setUp();
14 $this->custom_group = $this->customGroupCreate(['extends' => 'Case']);
15 $this->custom_group = $this->custom_group['values'][$this->custom_group['id']];
16 }
17
18 /**
19 * Test that changes to custom fields on cases generate the correct details
20 * body for ChangeCustomData.
21 *
22 * @dataProvider customDataProvider
23 *
24 * @param array $input
25 * @param array $expected
26 */
27 public function testChangeCustomDataFormattedDetails(array $input, array $expected) {
28 // set up custom field, with any overrides from input params
29 $custom_field = $this->callAPISuccess('custom_field', 'create', array_merge([
30 'custom_group_id' => $this->custom_group['id'],
31 'label' => 'What?',
32 'data_type' => 'String',
33 'html_type' => 'Text',
34 'is_active' => 1,
35 ], $input['custom_field_params']));
36 $custom_field = $custom_field['values'][$custom_field['id']];
37
38 // set up case and set the custom field initial value
39 $client_id = $this->individualCreate([], 0, TRUE);
40 $caseObj = $this->createCase($client_id, $this->_loggedInUser);
41 if (isset($input['custom_field_oldvalue'])) {
42 $this->callAPISuccess('CustomValue', 'create', [
43 "custom_{$custom_field['id']}" => $input['custom_field_oldvalue'],
44 'entity_id' => $caseObj->id,
45 ]);
46 }
47
48 // set up form
49 $form = $this->getFormObject('CRM_Case_Form_CustomData');
50 $form->set('groupID', $this->custom_group['id']);
51 $form->set('entityID', $caseObj->id);
52 $form->set('subType', $this->caseTypeId);
53 $form->set('cid', $client_id);
54 $form->preProcess();
55
56 // We need to do money conversion here because to do the creation above it
57 // needs to be in machine format, but then for the form stuff it needs to
58 // be in user format.
59 if (($input['custom_field_params']['data_type'] ?? '') === 'Money' && CRM_Core_I18n::singleton()->getLocale() !== 'en_US') {
60 $expected['string'] = $this->convertCurrency($expected['string']);
61 if (isset($input['custom_field_oldvalue'])) {
62 $input['custom_field_oldvalue'] = $this->convertCurrency($input['custom_field_oldvalue']);
63 }
64 $input['custom_field_newvalue'] = $this->convertCurrency($input['custom_field_newvalue']);
65 }
66
67 // Simulate an edit with formValues.
68 // The suffix is always going to be '1' since we created it above and it's
69 // the first entry in the custom_value_XX table. If it doesn't exist yet
70 // then our new entry will still be the first and will have suffix 1.
71 $custom_field_name = "custom_{$custom_field['id']}_1";
72 $formValues = [$custom_field_name => $input['custom_field_newvalue']];
73
74 // compute and compare
75 $output = $form->formatCustomDataChangesForDetail($formValues);
76 $this->assertEquals($expected['string'], $output);
77 }
78
79 /**
80 * Same as testChangeCustomDataFormattedDetails but in a different locale.
81 *
82 * @dataProvider customDataProvider
83 *
84 * @param array $input
85 * @param array $expected
86 */
87 public function testChangeCustomDataFormattedDetailsLocale(array $input, array $expected) {
88 CRM_Core_I18n::singleton()->setLocale('it_IT');
89 CRM_Core_Config::singleton()->defaultCurrency = 'EUR';
90 CRM_Core_Config::singleton()->monetaryThousandSeparator = ' ';
91 CRM_Core_Config::singleton()->monetaryDecimalPoint = ',';
92
93 $this->testChangeCustomDataFormattedDetails($input, $expected);
94
95 CRM_Core_Config::singleton()->defaultCurrency = 'USD';
96 CRM_Core_Config::singleton()->monetaryThousandSeparator = ',';
97 CRM_Core_Config::singleton()->monetaryDecimalPoint = '.';
98 CRM_Core_I18n::singleton()->setLocale('en_US');
99 }
100
101 /**
102 * data provider for testChangeCustomDataFormattedDetails
103 *
104 * @return array
105 */
106 public function customDataProvider(): array {
107 return [
108 0 => [
109 'input' => [
110 'custom_field_params' => [
111 'html_type' => 'Select',
112 'option_values' => [
113 [
114 'name' => 'Red',
115 'label' => 'Red',
116 'value' => '1',
117 'is_active' => 1,
118 'weight' => 1,
119 ],
120 [
121 'name' => 'Green',
122 'label' => 'Green',
123 'value' => '2',
124 'is_active' => 1,
125 'weight' => 2,
126 ],
127 ],
128 ],
129 'custom_field_oldvalue' => '1',
130 'custom_field_newvalue' => '2',
131 ],
132 'expected' => [
133 'string' => 'What?: Red => Green',
134 ],
135 ],
136
137 1 => [
138 'input' => [
139 'custom_field_params' => [
140 'html_type' => 'Select',
141 'option_values' => [
142 [
143 'name' => 'Red',
144 'label' => 'Red',
145 'value' => '1',
146 'is_active' => 1,
147 'weight' => 1,
148 ],
149 [
150 'name' => 'Green',
151 'label' => 'Green',
152 'value' => '2',
153 'is_active' => 1,
154 'weight' => 2,
155 ],
156 ],
157 ],
158 'custom_field_oldvalue' => '',
159 'custom_field_newvalue' => '2',
160 ],
161 'expected' => [
162 'string' => 'What?: => Green',
163 ],
164 ],
165
166 2 => [
167 'input' => [
168 'custom_field_params' => [
169 'html_type' => 'Select',
170 'option_values' => [
171 [
172 'name' => 'Red',
173 'label' => 'Red',
174 'value' => '1',
175 'is_active' => 1,
176 'weight' => 1,
177 ],
178 [
179 'name' => 'Green',
180 'label' => 'Green',
181 'value' => '2',
182 'is_active' => 1,
183 'weight' => 2,
184 ],
185 ],
186 ],
187 'custom_field_oldvalue' => '1',
188 'custom_field_newvalue' => '',
189 ],
190 'expected' => [
191 'string' => 'What?: Red => ',
192 ],
193 ],
194
195 3 => [
196 'input' => [
197 'custom_field_params' => [
198 'html_type' => 'Select',
199 'option_values' => [
200 [
201 'name' => 'Red',
202 'label' => 'Red',
203 'value' => '1',
204 'is_active' => 1,
205 'weight' => 1,
206 ],
207 [
208 'name' => 'Green',
209 'label' => 'Green',
210 'value' => '2',
211 'is_active' => 1,
212 'weight' => 2,
213 ],
214 ],
215 ],
216 // Note no old value, simulating as if we already have existing cases, but just added the field definition now.
217 'custom_field_newvalue' => '2',
218 ],
219 'expected' => [
220 'string' => 'What?: => Green',
221 ],
222 ],
223
224 4 => [
225 'input' => [
226 'custom_field_params' => [
227 'data_type' => 'Money',
228 ],
229 'custom_field_oldvalue' => '1.23',
230 'custom_field_newvalue' => '2.34',
231 ],
232 'expected' => [
233 'string' => 'What?: 1.23 => 2.34',
234 ],
235 ],
236
237 5 => [
238 'input' => [
239 'custom_field_params' => [
240 'data_type' => 'Money',
241 ],
242 'custom_field_oldvalue' => '',
243 'custom_field_newvalue' => '2.34',
244 ],
245 'expected' => [
246 'string' => 'What?: 0.00 => 2.34',
247 ],
248 ],
249
250 6 => [
251 'input' => [
252 'custom_field_params' => [
253 'data_type' => 'Money',
254 ],
255 'custom_field_oldvalue' => '1.23',
256 'custom_field_newvalue' => '',
257 ],
258 'expected' => [
259 'string' => 'What?: 1.23 => ',
260 ],
261 ],
262
263 7 => [
264 'input' => [
265 'custom_field_params' => [
266 'data_type' => 'Money',
267 ],
268 'custom_field_newvalue' => '2.34',
269 ],
270 'expected' => [
271 'string' => 'What?: => 2.34',
272 ],
273 ],
274
275 8 => [
276 'input' => [
277 'custom_field_params' => [],
278 'custom_field_oldvalue' => 'some text',
279 'custom_field_newvalue' => 'some new text',
280 ],
281 'expected' => [
282 'string' => 'What?: some text => some new text',
283 ],
284 ],
285
286 9 => [
287 'input' => [
288 'custom_field_params' => [],
289 'custom_field_oldvalue' => '',
290 'custom_field_newvalue' => 'some new text',
291 ],
292 'expected' => [
293 'string' => 'What?: => some new text',
294 ],
295 ],
296
297 10 => [
298 'input' => [
299 'custom_field_params' => [],
300 'custom_field_oldvalue' => 'some text',
301 'custom_field_newvalue' => '',
302 ],
303 'expected' => [
304 'string' => 'What?: some text => ',
305 ],
306 ],
307
308 11 => [
309 'input' => [
310 'custom_field_params' => [],
311 'custom_field_newvalue' => 'some new text',
312 ],
313 'expected' => [
314 'string' => 'What?: => some new text',
315 ],
316 ],
317 ];
318 }
319
320 /**
321 * Convert to locale currency format for purposes of these tests
322 * @param string $input
323 * @return string
324 */
325 private function convertCurrency(string $input): string {
326 $conversion_table = [
327 ',' => CRM_Core_Config::singleton()->monetaryThousandSeparator,
328 '.' => CRM_Core_Config::singleton()->monetaryDecimalPoint,
329 ];
330 return strtr($input, $conversion_table);
331 }
332
333 /**
334 * Test that when custom case data is edited but not changed that it doesn't
335 * create a meaningless empty activity.
336 */
337 public function testCustomDataNoChangeNoActivity(): void {
338 // Create a custom group and field
339 $customField = $this->callAPISuccess('custom_field', 'create', [
340 'custom_group_id' => $this->custom_group['id'],
341 'label' => 'What?',
342 'data_type' => 'String',
343 'html_type' => 'Text',
344 'is_active' => 1,
345 ]);
346 $customField = $customField['values'][$customField['id']];
347
348 // Create a case and set the custom field to something
349 $individual = $this->individualCreate();
350 $caseObj = $this->createCase($individual, $this->_loggedInUser);
351 $caseId = $caseObj->id;
352 $this->callAPISuccess('CustomValue', 'create', [
353 "custom_{$customField['id']}" => 'immutable text',
354 'entity_id' => $caseId,
355 ]);
356
357 // run the form
358 $form = new CRM_Case_Form_CustomData();
359 $form->controller = new CRM_Core_Controller_Simple('CRM_Case_Form_CustomData', 'Case Data');
360 $form->set('groupID', $this->custom_group['id']);
361 $form->set('entityID', $caseId);
362 // this is case type
363 $form->set('subType', 1);
364 $form->set('cid', $individual);
365 $form->buildForm();
366 ob_start();
367 $form->controller->_actions['display']->perform($form, 'display');
368 ob_end_clean();
369
370 // Don't change any field values and now call postProcess.
371 // Because the form does a redirect it triggers an exception during unit
372 // tests but that's ok.
373 $hadException = FALSE;
374 try {
375 $form->controller->_actions['upload']->perform($form, 'upload');
376 }
377 catch (CRM_Core_Exception_PrematureExitException $e) {
378 $hadException = TRUE;
379 $this->assertEquals('redirect', $e->errorData['context']);
380 // compare parts of query string separately to avoid any clean url format mismatches
381 $this->assertStringContainsString('civicrm/contact/view/case', $e->errorData['url']);
382 $this->assertStringContainsString("reset=1&id={$caseId}&cid={$individual}&action=view", $e->errorData['url']);
383 }
384 // Make sure we did have an exception since otherwise we might get a
385 // false pass for the asserts above since they'd never run.
386 $this->assertTrue($hadException);
387
388 // there shouldn't be an activity of type Change Custom Data on the case
389 $result = $this->callAPISuccess('Activity', 'get', [
390 'activity_type_id' => 'Change Custom Data',
391 'case_id' => $caseId,
392 ]);
393 $this->assertEquals(0, $result['count']);
394 }
395
396 }