be4e17b2710603c4e8b437c4508b392735f1851b
[civicrm-core.git] / tests / phpunit / api / v3 / CustomValueTest.php
1 <?php
2 /**
3 * +--------------------------------------------------------------------+
4 * | CiviCRM version 4.7 |
5 * +--------------------------------------------------------------------+
6 * | Copyright CiviCRM LLC (c) 2004-2015 |
7 * +--------------------------------------------------------------------+
8 * | This file is a part of CiviCRM. |
9 * | |
10 * | CiviCRM is free software; you can copy, modify, and distribute it |
11 * | under the terms of the GNU Affero General Public License |
12 * | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 * | |
14 * | CiviCRM is distributed in the hope that it will be useful, but |
15 * | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 * | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 * | See the GNU Affero General Public License for more details. |
18 * | |
19 * | You should have received a copy of the GNU Affero General Public |
20 * | License and the CiviCRM Licensing Exception along |
21 * | with this program; if not, contact CiviCRM LLC |
22 * | at info[AT]civicrm[DOT]org. If you have questions about the |
23 * | GNU Affero General Public License or the licensing of CiviCRM, |
24 * | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 * +--------------------------------------------------------------------+
26 */
27
28 require_once 'CiviTest/CiviUnitTestCase.php';
29
30 /**
31 * Class api_v3_CustomValueTest
32 */
33 class api_v3_CustomValueTest extends CiviUnitTestCase {
34 protected $_apiversion = 3;
35 protected $ids;
36 protected $optionGroup;
37
38 public $DBResetRequired = FALSE;
39
40 public function setUp() {
41 parent::setUp();
42
43 $this->_populateOptionAndCustomGroup();
44 }
45
46 public function _populateOptionAndCustomGroup() {
47 $dataValues = array(
48 'integer' => array(1, 2, 3),
49 'number' => array(10.11, 20.22, 30.33),
50 'string' => array(substr(sha1(rand()), 0, 4), substr(sha1(rand()), 0, 3), substr(sha1(rand()), 0, 2)),
51 'country' => array_rand(CRM_Core_PseudoConstant::country(FALSE, FALSE), 3),
52 'state_province' => array_rand(CRM_Core_PseudoConstant::stateProvince(FALSE, FALSE), 3),
53 'date' => NULL,
54 'contact' => NULL,
55 );
56
57 foreach ($dataValues as $dataType => $values) {
58 $this->optionGroup[$dataType] = array('values' => $values);
59 if (!empty($values)) {
60 $result = $this->callAPISuccess('OptionGroup', 'create',
61 array(
62 'name' => "{$dataType}_group",
63 'api.option_value.create' => array('label' => "$dataType 1", 'value' => $values[0]),
64 'api.option_value.create.1' => array('label' => "$dataType 2", 'value' => $values[1]),
65 'api.option_value.create.2' => array('label' => "$dataType 3", 'value' => $values[2]),
66 )
67 );
68 $this->optionGroup[$dataType]['id'] = $result['id'];
69 }
70 elseif ($dataType == 'contact') {
71 for ($i = 0; $i < 3; $i++) {
72 $result = $this->callAPISuccess('Contact', 'create', array('contact_type' => 'Individual', 'email' => substr(sha1(rand()), 0, 7) . '@yahoo.com'));
73 $this->optionGroup[$dataType]['values'][$i] = $result['id'];
74 }
75 }
76 $this->ids[$dataType] = $this->entityCustomGroupWithSingleFieldCreate("$dataType Custom Group", 'Contacts');
77 }
78
79 }
80
81 public function tearDown() {
82 $tablesToTruncate = array(
83 'civicrm_email',
84 'civicrm_custom_field',
85 'civicrm_custom_group',
86 'civicrm_contact',
87 );
88
89 // true tells quickCleanup to drop any tables that might have been created in the test
90 $this->quickCleanup($tablesToTruncate, TRUE);
91 }
92
93 public function testCreateCustomValue() {
94 $customFieldDataType = CRM_Core_BAO_CustomField::dataType();
95 $dataToHtmlTypes = CRM_Core_BAO_CustomField::dataToHtml();
96 $count = 0;
97
98 foreach ($customFieldDataType as $dataType => $label) {
99 switch ($dataType) {
100 case 'Date':
101 case 'StateProvince';
102 case 'String':
103 case 'Link':
104 case 'Int':
105 case 'Float':
106 case 'Money':
107 if (in_array($dataType, array('String', 'Link'))) {
108 $validSQLOperator = array('=', '!=', 'IN', 'NOT IN', 'LIKE', 'NOT LIKE', 'IS NOT NULL', 'IS NULL');
109 $type = 'string';
110 }
111 else {
112 if ($dataType == 'Country') {
113 $type == 'country';
114 }
115 elseif ($dataType == 'StateProvince') {
116 $type = 'state_province';
117 }
118 elseif ($dataType == 'ContactReference') {
119 $type = 'contact';
120 }
121 elseif ($dataType == 'Date') {
122 $type = 'date';
123 }
124 else {
125 $type = $dataType == 'Int' ? 'integer' : 'number';
126 }
127 $validSQLOperator = array('=', '!=', 'IN', 'NOT IN', '<=', '>=', '>', '<', 'IS NOT NULL', 'IS NULL');
128 }
129
130 foreach ($dataToHtmlTypes[$count] as $html) {
131 $params = array(
132 'custom_group_id' => $this->ids[$type]['custom_group_id'],
133 'label' => "$dataType - $html",
134 'data_type' => $dataType,
135 'html_type' => $html,
136 'default_value' => NULL,
137 );
138 if (!in_array($html, array('Text', 'TextArea')) && !in_array($dataType, array('Link', 'Date', 'ContactReference'))) {
139 $params += array('option_group_id' => $this->optionGroup[$type]['id']);
140 }
141 $customField = $this->customFieldCreate($params);
142 $this->_testCustomValue($customField['values'][$customField['id']], $validSQLOperator, $type);
143 }
144 $count++;
145 break;
146
147 default:
148 //TODO: Test case of Country fields remain as it throws foreign key contraint ONLY in test environment
149 $count++;
150 break;
151 }
152 }
153 }
154
155 public function _testCustomValue($customField, $sqlOps, $type) {
156 $isSerialized = CRM_Core_BAO_CustomField::isSerialized($customField);
157 $customId = $customField['id'];
158 $params = array(
159 'contact_type' => 'Individual',
160 'email' => substr(sha1(rand()), 0, 7) . 'man1@yahoo.com',
161 );
162 $result = $this->callAPISuccess('Contact', 'create', $params);
163 $contactId = $result['id'];
164
165 $count = rand(1, 2);
166 $seperator = CRM_Core_DAO::VALUE_SEPARATOR;
167 if ($isSerialized) {
168 $selectedValue = $this->optionGroup[$type]['values'];
169 $notselectedValue = $selectedValue[$count];
170 unset($selectedValue[$count]);
171 }
172 elseif ($customField['html_type'] == 'Link') {
173 $selectedValue = "http://" . substr(sha1(rand()), 0, 7) . ".com";
174 $notselectedValue = "http://" . substr(sha1(rand()), 0, 7) . ".com";
175 }
176 elseif ($type == 'date') {
177 $selectedValue = date('Ymd');
178 $notselectedValue = $lesserSelectedValue = date('Ymd', strtotime('yesterday'));
179 $greaterSelectedValue = date('Ymd', strtotime('+ 1 day'));
180 }
181 elseif ($type == 'contact') {
182 $selectedValue = $this->optionGroup[$type]['values'][1];
183 $notselectedValue = $this->optionGroup[$type]['values'][0];
184 }
185 else {
186 $selectedValue = $this->optionGroup[$type]['values'][0];
187 $notselectedValue = $this->optionGroup[$type]['values'][$count];
188 if (in_array(">", $sqlOps)) {
189 $greaterSelectedValue = $selectedValue + 1;
190 $lesserSelectedValue = $selectedValue - 1;
191 }
192 }
193
194 $params = array('entity_id' => $contactId, 'custom_' . $customId => $selectedValue);
195 $this->callAPISuccess('CustomValue', 'create', $params);
196
197 foreach ($sqlOps as $op) {
198 $qillOp = CRM_Utils_Array::value($op, CRM_Core_SelectValues::getSearchBuilderOperators(), $op);
199 $description = "\nFind Contact where '$customField[label]' $qillOp ";
200 switch ($op) {
201 case '=':
202 $result = $this->callAPISuccess('Contact', 'Get', array('custom_' . $customId => (is_array($selectedValue) ? implode(CRM_Core_DAO::VALUE_SEPARATOR, $selectedValue) : $selectedValue)));
203 $this->assertEquals($contactId, $result['id']);
204 echo $description . implode("[separator]", (array) $selectedValue);
205 break;
206
207 case '!=':
208 $result = $this->callAPISuccess('Contact', 'Get', array('custom_' . $customId => array($op => $notselectedValue)));
209 $this->assertEquals(TRUE, array_key_exists($contactId, $result['values']));
210 echo $description . $notselectedValue;
211 break;
212
213 case '>':
214 case '<':
215 case '>=':
216 case '<=':
217 if ($isSerialized) {
218 continue;
219 }
220 // To be precise in for these operator we can't just rely on one contact,
221 // hence creating multiple contact with custom value less/more then $selectedValue respectively
222 $result = $this->callAPISuccess('Contact', 'create', array('contact_type' => 'Individual', 'email' => substr(sha1(rand()), 0, 7) . 'man2@yahoo.com'));
223 $contactId2 = $result['id'];
224 $this->callAPISuccess('CustomValue', 'create', array('entity_id' => $contactId2, 'custom_' . $customId => $lesserSelectedValue));
225
226 if ($op == '>') {
227 $result = $this->callAPISuccess('Contact', 'Get', array('custom_' . $customId => array($op => $lesserSelectedValue)));
228 $this->assertEquals($contactId, $result['id']);
229 echo $description . $lesserSelectedValue;
230 }
231 elseif ($op == '<') {
232 $result = $this->callAPISuccess('Contact', 'Get', array('custom_' . $customId => array($op => $selectedValue)));
233 $this->assertEquals($contactId2, $result['id']);
234 echo $description . $selectedValue;
235 }
236 else {
237 $result = $this->callAPISuccess('Contact', 'create', array('contact_type' => 'Individual', 'email' => substr(sha1(rand()), 0, 7) . 'man3@yahoo.com'));
238 $contactId3 = $result['id'];
239 $this->callAPISuccess('CustomValue', 'create', array('entity_id' => $contactId3, 'custom_' . $customId => $greaterSelectedValue));
240
241 $result = $this->callAPISuccess('Contact', 'Get', array('custom_' . $customId => array($op => $selectedValue)));
242 echo $description . $selectedValue;
243
244 $this->assertEquals($contactId, $result['values'][$contactId]['id']);
245 if ($op == '>=') {
246 $this->assertEquals($contactId3, $result['values'][$contactId3]['id']);
247 }
248 else {
249 $this->assertEquals($contactId2, $result['values'][$contactId2]['id']);
250 }
251 $this->callAPISuccess('contact', 'delete', array('id' => $contactId3));
252 }
253
254 $this->callAPISuccess('contact', 'delete', array('id' => $contactId2));
255 break;
256
257 case 'IN':
258 $result = $this->callAPISuccess('Contact', 'Get', array('custom_' . $customId => array($op => (array) $selectedValue)));
259 $this->assertEquals($contactId, $result['id']);
260 echo $description . implode(",", (array) $selectedValue);
261 break;
262
263 case 'NOT IN':
264 $result = $this->callAPISuccess('Contact', 'Get', array('custom_' . $customId => array($op => (array) $notselectedValue)));
265 $this->assertEquals($contactId, $result['id']);
266 echo $description . implode(",", (array) $notselectedValue);
267 break;
268
269 case 'LIKE':
270 $selectedValue = is_array($selectedValue) ? $selectedValue[0] : $selectedValue;
271 $result = $this->callAPISuccess('Contact', 'Get', array('custom_' . $customId => array($op => "%$selectedValue%")));
272 $this->assertEquals($contactId, $result['id']);
273 echo $description . "%$selectedValue%";
274 break;
275
276 case 'NOT LIKE':
277 $result = $this->callAPISuccess('Contact', 'Get', array('custom_' . $customId => array($op => $notselectedValue)));
278 $this->assertEquals($contactId, $result['id']);
279 echo $description . "'$notselectedValue'";
280 break;
281
282 case 'IS NULL':
283 $result = $this->callAPISuccess('Contact', 'Get', array('custom_' . $customId => array($op => 1)));
284 $this->assertEquals(FALSE, array_key_exists($contactId, $result['values']));
285 echo $description;
286 break;
287
288 case 'IS NOT NULL':
289 $result = $this->callAPISuccess('Contact', 'Get', array('custom_' . $customId => array($op => 1)));
290 $this->assertEquals($contactId, $result['id']);
291 echo $description;
292 break;
293 }
294 }
295
296 $this->callAPISuccess('Contact', 'delete', array('id' => $contactId));
297 }
298
299 /**
300 * Ensure custom data is updated when option values are modified
301 *
302 * @link https://issues.civicrm.org/jira/browse/CRM-11856
303 *
304 * @throws \CiviCRM_API3_Exception
305 */
306 public function testAlterOptionValue() {
307 $selectField = $this->customFieldCreate(array(
308 'custom_group_id' => $this->ids['single']['custom_group_id'],
309 'label' => 'Custom Select',
310 'html_type' => 'Select',
311 'option_values' => array(
312 'one' => 'Option1',
313 'two' => 'Option2',
314 'notone' => 'OptionNotOne',
315 ),
316 ));
317 $selectField = civicrm_api3('customField', 'getsingle', array('id' => $selectField['id']));
318 $radioField = $this->customFieldCreate(array(
319 'custom_group_id' => $this->ids['single']['custom_group_id'],
320 'label' => 'Custom Radio',
321 'html_type' => 'Radio',
322 'option_group_id' => $selectField['option_group_id'],
323 ));
324 $multiSelectField = $this->customFieldCreate(array(
325 'custom_group_id' => $this->ids['single']['custom_group_id'],
326 'label' => 'Custom Multi-Select',
327 'html_type' => 'Multi-Select',
328 'option_group_id' => $selectField['option_group_id'],
329 ));
330 $selectName = 'custom_' . $selectField['id'];
331 $radioName = 'custom_' . $radioField['id'];
332 $multiSelectName = 'custom_' . $multiSelectField['id'];
333 $controlFieldName = 'custom_' . $this->ids['single']['custom_field_id'];
334
335 $params = array(
336 'first_name' => 'abc4',
337 'last_name' => 'xyz4',
338 'contact_type' => 'Individual',
339 'email' => 'man4@yahoo.com',
340 $selectName => 'one',
341 $multiSelectName => array('one', 'two', 'notone'),
342 $radioName => 'notone',
343 // The control group in a science experiment should be unaffected
344 $controlFieldName => 'one',
345 );
346
347 $contact = $this->callAPISuccess('Contact', 'create', $params);
348
349 $result = $this->callAPISuccess('Contact', 'getsingle', array(
350 'id' => $contact['id'],
351 'return' => array($selectName, $multiSelectName),
352 ));
353 $this->assertEquals('one', $result[$selectName]);
354 $this->assertEquals(array('one', 'two', 'notone'), $result[$multiSelectName]);
355
356 $this->callAPISuccess('OptionValue', 'create', array(
357 'value' => 'one-modified',
358 'option_group_id' => $selectField['option_group_id'],
359 'name' => 'Option1',
360 'options' => array(
361 'match-mandatory' => array('option_group_id', 'name'),
362 ),
363 ));
364
365 $result = $this->callAPISuccess('Contact', 'getsingle', array(
366 'id' => $contact['id'],
367 'return' => array($selectName, $multiSelectName, $controlFieldName, $radioName),
368 ));
369 // Ensure the relevant fields have been updated
370 $this->assertEquals('one-modified', $result[$selectName]);
371 $this->assertEquals(array('one-modified', 'two', 'notone'), $result[$multiSelectName]);
372 // This field should not have changed because we didn't alter this option
373 $this->assertEquals('notone', $result[$radioName]);
374 // This should not have changed because this field doesn't use the affected option group
375 $this->assertEquals('one', $result[$controlFieldName]);
376 }
377
378 }