APIv4 - Fix saving NULL as custom field value
[civicrm-core.git] / tests / phpunit / api / v4 / Action / PseudoconstantTest.php
1 <?php
2
3 /*
4 +--------------------------------------------------------------------+
5 | Copyright CiviCRM LLC. All rights reserved. |
6 | |
7 | This work is published under the GNU AGPLv3 license with some |
8 | permitted exceptions and without any warranty. For full license |
9 | and copyright information, see https://civicrm.org/licensing |
10 +--------------------------------------------------------------------+
11 */
12
13 /**
14 *
15 * @package CRM
16 * @copyright CiviCRM LLC https://civicrm.org/licensing
17 */
18
19
20 namespace api\v4\Action;
21
22 use Civi\Api4\Address;
23 use Civi\Api4\Campaign;
24 use Civi\Api4\Contact;
25 use Civi\Api4\Activity;
26 use Civi\Api4\Contribution;
27 use Civi\Api4\CustomField;
28 use Civi\Api4\CustomGroup;
29 use Civi\Api4\Email;
30 use Civi\Api4\EntityTag;
31 use Civi\Api4\OptionValue;
32 use Civi\Api4\Participant;
33 use Civi\Api4\Tag;
34
35 /**
36 * @group headless
37 */
38 class PseudoconstantTest extends BaseCustomValueTest {
39
40 public function testOptionValue() {
41 $cid = Contact::create(FALSE)->addValue('first_name', 'bill')->execute()->first()['id'];
42 $subject = uniqid('subject');
43 OptionValue::create()
44 ->addValue('option_group_id:name', 'activity_type')
45 ->addValue('label', 'Fake Type')
46 ->execute();
47
48 $options = Activity::getFields()
49 ->addWhere('name', '=', 'activity_type_id')
50 ->setLoadOptions(['id', 'name', 'label'])
51 ->execute()->first()['options'];
52 $options = array_column($options, NULL, 'name');
53 $this->assertEquals('Fake Type', $options['Fake_Type']['label']);
54
55 Activity::create()
56 ->addValue('activity_type_id:name', 'Meeting')
57 ->addValue('source_contact_id', $cid)
58 ->addValue('subject', $subject)
59 ->execute();
60
61 Activity::create()
62 ->addValue('activity_type_id:name', 'Fake_Type')
63 ->addValue('source_contact_id', $cid)
64 ->addValue('subject', $subject)
65 ->execute();
66
67 $act = Activity::get()
68 ->addWhere('activity_type_id:label', '=', 'Fake Type')
69 ->addWhere('subject', '=', $subject)
70 ->addSelect('activity_type_id:name')
71 ->addSelect('activity_type_id:label')
72 ->addSelect('activity_type_id')
73 ->execute();
74
75 $this->assertCount(1, $act);
76 $this->assertEquals('Fake Type', $act[0]['activity_type_id:label']);
77 $this->assertEquals('Fake_Type', $act[0]['activity_type_id:name']);
78 $this->assertTrue(is_numeric($act[0]['activity_type_id']));
79
80 $act = Activity::get()
81 ->addHaving('activity_type_id:name', '=', 'Fake_Type')
82 ->addHaving('subject', '=', $subject)
83 ->addSelect('activity_type_id:label')
84 ->addSelect('activity_type_id')
85 ->addSelect('subject')
86 ->execute();
87
88 $this->assertCount(1, $act);
89 $this->assertEquals('Fake Type', $act[0]['activity_type_id:label']);
90 $this->assertTrue(is_numeric($act[0]['activity_type_id']));
91
92 $act = Activity::get()
93 ->addHaving('activity_type_id:name', '=', 'Fake_Type')
94 ->addHaving('subject', '=', $subject)
95 ->addSelect('activity_type_id')
96 ->addSelect('subject')
97 ->execute();
98
99 $this->assertCount(1, $act);
100 $this->assertTrue(is_numeric($act[0]['activity_type_id']));
101 }
102
103 public function testAddressOptions() {
104 $cid = Contact::create(FALSE)->addValue('first_name', 'addr')->execute()->first()['id'];
105 Address::save()
106 ->addRecord([
107 'contact_id' => $cid,
108 'state_province_id:abbr' => 'CA',
109 'country_id:label' => 'United States',
110 'street_address' => '1',
111 ])
112 ->addRecord([
113 'contact_id' => $cid,
114 'state_province_id:abbr' => 'CA',
115 'country_id:label' => 'Uruguay',
116 'street_address' => '2',
117 ])
118 ->addRecord([
119 'contact_id' => $cid,
120 'state_province_id:abbr' => 'CA',
121 'country_id:abbr' => 'ES',
122 'street_address' => '3',
123 ])
124 ->execute();
125
126 $addr = Address::get()
127 ->addWhere('contact_id', '=', $cid)
128 ->addSelect('state_province_id:abbr', 'state_province_id:name', 'country_id:label', 'country_id:name')
129 ->addOrderBy('street_address')
130 ->execute();
131
132 $this->assertCount(3, $addr);
133
134 // US - California
135 $this->assertEquals('CA', $addr[0]['state_province_id:abbr']);
136 $this->assertEquals('California', $addr[0]['state_province_id:name']);
137 $this->assertEquals('US', $addr[0]['country_id:name']);
138 $this->assertEquals('United States', $addr[0]['country_id:label']);
139 // Uruguay - Canelones
140 $this->assertEquals('CA', $addr[1]['state_province_id:abbr']);
141 $this->assertEquals('Canelones', $addr[1]['state_province_id:name']);
142 $this->assertEquals('UY', $addr[1]['country_id:name']);
143 $this->assertEquals('Uruguay', $addr[1]['country_id:label']);
144 // Spain - Cádiz
145 $this->assertEquals('CA', $addr[2]['state_province_id:abbr']);
146 $this->assertEquals('Cádiz', $addr[2]['state_province_id:name']);
147 $this->assertEquals('ES', $addr[2]['country_id:name']);
148 $this->assertEquals('Spain', $addr[2]['country_id:label']);
149 }
150
151 public function testCustomOptions() {
152 $technicolor = [
153 ['id' => 'r', 'name' => 'red', 'label' => 'RED', 'color' => '#ff0000', 'description' => 'Red color', 'icon' => 'fa-red'],
154 ['id' => 'g', 'name' => 'green', 'label' => 'GREEN', 'color' => '#00ff00', 'description' => 'Green color', 'icon' => 'fa-green'],
155 ['id' => 'b', 'name' => 'blue', 'label' => 'BLUE', 'color' => '#0000ff', 'description' => 'Blue color', 'icon' => 'fa-blue'],
156 ];
157
158 CustomGroup::create(FALSE)
159 ->addValue('name', 'myPseudoconstantTest')
160 ->addValue('extends', 'Individual')
161 ->addChain('field1', CustomField::create()
162 ->addValue('custom_group_id', '$id')
163 ->addValue('option_values', ['r' => 'red', 'g' => 'green', 'b' => 'blü'])
164 ->addValue('label', 'Color')
165 ->addValue('html_type', 'Select')
166 )->addChain('field2', CustomField::create()
167 ->addValue('custom_group_id', '$id')
168 ->addValue('option_values', $technicolor)
169 ->addValue('label', 'Technicolor')
170 ->addValue('html_type', 'CheckBox')
171 )->execute();
172
173 $fields = Contact::getFields()
174 ->setLoadOptions(array_keys($technicolor[0]))
175 ->execute()
176 ->indexBy('name');
177
178 foreach ($technicolor as $index => $option) {
179 foreach ($option as $prop => $val) {
180 $this->assertEquals($val, $fields['myPseudoconstantTest.Technicolor']['options'][$index][$prop]);
181 }
182 }
183
184 $cid = Contact::create(FALSE)
185 ->addValue('first_name', 'col')
186 ->addValue('myPseudoconstantTest.Color:label', 'blü')
187 ->execute()->first()['id'];
188
189 $result = Contact::get(FALSE)
190 ->addWhere('id', '=', $cid)
191 ->addSelect('myPseudoconstantTest.Color:name', 'myPseudoconstantTest.Color:label', 'myPseudoconstantTest.Color')
192 ->execute()->first();
193
194 $this->assertEquals('blü', $result['myPseudoconstantTest.Color:label']);
195 $this->assertEquals('bl_', $result['myPseudoconstantTest.Color:name']);
196 $this->assertEquals('b', $result['myPseudoconstantTest.Color']);
197
198 $cid1 = Contact::create(FALSE)
199 ->addValue('first_name', 'two')
200 ->addValue('myPseudoconstantTest.Technicolor:label', 'RED')
201 ->execute()->first()['id'];
202 $cid2 = Contact::create(FALSE)
203 ->addValue('first_name', 'two')
204 ->addValue('myPseudoconstantTest.Technicolor:label', 'GREEN')
205 ->execute()->first()['id'];
206
207 // Test ordering by label
208 $result = Contact::get(FALSE)
209 ->addWhere('id', 'IN', [$cid1, $cid2])
210 ->addSelect('id')
211 ->addOrderBy('myPseudoconstantTest.Technicolor:label')
212 ->execute()->first()['id'];
213 $this->assertEquals($cid2, $result);
214 $result = Contact::get(FALSE)
215 ->addWhere('id', 'IN', [$cid1, $cid2])
216 ->addSelect('id')
217 ->addOrderBy('myPseudoconstantTest.Technicolor:label', 'DESC')
218 ->execute()->first()['id'];
219 $this->assertEquals($cid1, $result);
220 }
221
222 public function testJoinOptions() {
223 $cid1 = Contact::create(FALSE)
224 ->addValue('first_name', 'Tom')
225 ->addValue('gender_id:label', 'Male')
226 ->addChain('email', Email::create()->setValues(['contact_id' => '$id', 'email' => 'tom@example.com', 'location_type_id:name' => 'Work']))
227 ->execute()->first()['id'];
228 $cid2 = Contact::create(FALSE)
229 ->addValue('first_name', 'Sue')
230 ->addValue('gender_id:name', 'Female')
231 ->addChain('email', Email::create()->setValues(['contact_id' => '$id', 'email' => 'sue@example.com', 'location_type_id:name' => 'Home']))
232 ->execute()->first()['id'];
233 $cid3 = Contact::create(FALSE)
234 ->addValue('first_name', 'Pat')
235 ->addChain('email', Email::create()->setValues(['contact_id' => '$id', 'email' => 'pat@example.com', 'location_type_id:name' => 'Home']))
236 ->execute()->first()['id'];
237
238 $emails = Email::get()
239 ->addSelect('location_type_id:name', 'contact_id.gender_id:label', 'email', 'contact_id')
240 ->addWhere('contact_id', 'IN', [$cid1, $cid2, $cid3])
241 ->addWhere('contact_id.gender_id:label', 'IN', ['Male', 'Female'])
242 ->execute()->indexBy('contact_id');
243 $this->assertCount(2, $emails);
244 $this->assertEquals('Work', $emails[$cid1]['location_type_id:name']);
245 $this->assertEquals('Home', $emails[$cid2]['location_type_id:name']);
246 $this->assertEquals('Male', $emails[$cid1]['contact_id.gender_id:label']);
247 $this->assertEquals('Female', $emails[$cid2]['contact_id.gender_id:label']);
248
249 $emails = Email::get()
250 ->addSelect('location_type_id:name', 'contact_id.gender_id:label', 'email', 'contact_id')
251 ->addWhere('contact_id', 'IN', [$cid1, $cid2, $cid3])
252 ->addWhere('location_type_id:name', 'IN', ['Home'])
253 ->execute()->indexBy('contact_id');
254 $this->assertCount(2, $emails);
255 $this->assertEquals('Home', $emails[$cid2]['location_type_id:name']);
256 $this->assertEquals('Home', $emails[$cid3]['location_type_id:name']);
257 $this->assertEquals('Female', $emails[$cid2]['contact_id.gender_id:label']);
258 $this->assertNull($emails[$cid3]['contact_id.gender_id:label']);
259 }
260
261 public function testTagOptions() {
262 $tag = uniqid('tag');
263 Tag::create(FALSE)
264 ->addValue('name', $tag)
265 ->addValue('description', 'colorful')
266 ->addValue('color', '#aabbcc')
267 ->execute();
268 $options = EntityTag::getFields()
269 ->setLoadOptions(['id', 'name', 'color', 'description', 'label'])
270 ->addWhere('name', '=', 'tag_id')
271 ->execute()->first()['options'];
272 $options = array_column($options, NULL, 'name');
273 $this->assertEquals('colorful', $options[$tag]['description']);
274 $this->assertEquals('#aabbcc', $options[$tag]['color']);
275 $this->assertEquals($tag, $options[$tag]['label']);
276 }
277
278 public function testParticipantRole() {
279 $event = $this->createEntity(['type' => 'Event']);
280 $contact = $this->createEntity(['type' => 'Individual']);
281 $participant = Participant::create()
282 ->addValue('contact_id', $contact['id'])
283 ->addValue('event_id', $event['id'])
284 ->addValue('role_id:label', ['Attendee', 'Volunteer'])
285 ->execute()->first();
286
287 $search1 = Participant::get()
288 ->addSelect('role_id', 'role_id:label')
289 ->addWhere('role_id:label', 'CONTAINS', 'Volunteer')
290 ->addOrderBy('id')
291 ->execute()->last();
292
293 $this->assertEquals(['Attendee', 'Volunteer'], $search1['role_id:label']);
294 $this->assertEquals(['1', '2'], $search1['role_id']);
295
296 $search2 = Participant::get()
297 ->addWhere('role_id:label', 'CONTAINS', 'Host')
298 ->execute()->indexBy('id');
299
300 $this->assertArrayNotHasKey($participant['id'], (array) $search2);
301 }
302
303 public function testPreloadFalse() {
304 \CRM_Core_BAO_ConfigSetting::enableComponent('CiviContribute');
305 \CRM_Core_BAO_ConfigSetting::enableComponent('CiviCampaign');
306
307 $contact = $this->createEntity(['type' => 'Individual']);
308
309 $campaignTitle = uniqid('Test ');
310
311 $campaignId = Campaign::create(FALSE)
312 ->addValue('title', $campaignTitle)
313 ->addValue('campaign_type_id', 1)
314 ->execute()->first()['id'];
315
316 $contributionId = Contribution::create(FALSE)
317 ->addValue('campaign_id', $campaignId)
318 ->addValue('contact_id', $contact['id'])
319 ->addValue('financial_type_id', 1)
320 ->addValue('total_amount', .01)
321 ->execute()->first()['id'];
322
323 // Even though the option list of campaigns is not available (prefetch = false)
324 // We should still be able to get the title of the campaign as :label
325 $result = Contribution::get(FALSE)
326 ->addWhere('id', '=', $contributionId)
327 ->addSelect('campaign_id:label')
328 ->execute()->single();
329
330 $this->assertEquals($campaignTitle, $result['campaign_id:label']);
331
332 // Fetching the title via join ought to work too
333 $result = Contribution::get(FALSE)
334 ->addWhere('id', '=', $contributionId)
335 ->addSelect('campaign_id.title')
336 ->execute()->single();
337
338 $this->assertEquals($campaignTitle, $result['campaign_id.title']);
339 }
340
341 }