Commit | Line | Data |
---|---|---|
6a488035 | 1 | <?php |
6a488035 | 2 | /* |
a30c801b TO |
3 | +--------------------------------------------------------------------+ |
4 | | Copyright CiviCRM LLC. All rights reserved. | | |
5 | | | | |
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 | | |
9 | +--------------------------------------------------------------------+ | |
e70a7fc0 | 10 | */ |
6a488035 TO |
11 | |
12 | /** | |
b081365f | 13 | * This api exposes CiviCRM Case objects. |
6a488035 TO |
14 | * Developed by woolman.org |
15 | * | |
16 | * @package CiviCRM_APIv3 | |
6a488035 TO |
17 | */ |
18 | ||
6a488035 | 19 | /** |
244bbdd8 | 20 | * Open a new case, add client and manager roles, and standard timeline. |
6a488035 | 21 | * |
6c552737 | 22 | * @param array $params |
37b8953e | 23 | * |
0b882a86 | 24 | * ``` |
ea2aa8ff | 25 | * // REQUIRED for create: |
6a488035 TO |
26 | * 'case_type_id' => int OR |
27 | * 'case_type' => str (provide one or the other) | |
28 | * 'contact_id' => int // case client | |
29 | * 'subject' => str | |
ea2aa8ff | 30 | * // REQUIRED for update: |
791a8dc4 | 31 | * 'id' => case Id |
6a488035 TO |
32 | * |
33 | * //OPTIONAL | |
34 | * 'medium_id' => int // see civicrm option values for possibilities | |
35 | * 'creator_id' => int // case manager, default to the logged in user | |
36 | * 'status_id' => int // defaults to 1 "ongoing" | |
37 | * 'location' => str | |
38 | * 'start_date' => str datestamp // defaults to: date('YmdHis') | |
39 | * 'duration' => int // in minutes | |
40 | * 'details' => str // html format | |
0b882a86 | 41 | * ``` |
6a488035 | 42 | * |
784e85a1 | 43 | * @throws API_Exception |
a6c01b45 | 44 | * @return array |
72b3a70c | 45 | * api result array |
6a488035 TO |
46 | */ |
47 | function civicrm_api3_case_create($params) { | |
6a488035 TO |
48 | _civicrm_api3_case_format_params($params); |
49 | ||
da4d8910 MW |
50 | if (empty($params['id'])) { |
51 | // Creating a new case, so make sure we have the necessary parameters | |
cf8f0fff | 52 | civicrm_api3_verify_mandatory($params, NULL, [ |
7c31ae57 SL |
53 | 'contact_id', |
54 | 'subject', | |
55 | ['case_type', 'case_type_id'], | |
56 | ]); | |
6a488035 | 57 | } |
da4d8910 MW |
58 | else { |
59 | // Update an existing case | |
60 | // FIXME: Some of this logic should move to the BAO object? | |
61 | // FIXME: Should we check if case with ID actually exists? | |
6a488035 | 62 | |
da4d8910 | 63 | if (array_key_exists('creator_id', $params)) { |
ef76301b | 64 | throw new API_Exception('You cannot update creator id'); |
da4d8910 MW |
65 | } |
66 | ||
cf8f0fff | 67 | $mergedCaseIds = $origContactIds = []; |
da4d8910 | 68 | |
ea2aa8ff | 69 | // If a contact ID is specified we need to make sure this is the main contact ID for the case (and update if necessary) |
da4d8910 MW |
70 | if (!empty($params['contact_id'])) { |
71 | $origContactIds = CRM_Case_BAO_Case::retrieveContactIdsByCaseId($params['id']); | |
da4d8910 | 72 | |
ea2aa8ff MW |
73 | // Get the original contact ID for the case |
74 | // FIXME: Refactor as separate method to get contactId | |
75 | if (count($origContactIds) > 1) { | |
76 | // Multiple original contact IDs. Need to specify which one to use as a parameter | |
77 | if (empty($params['orig_contact_id'])) { | |
78 | throw new API_Exception('Case is linked with more than one contact id. Provide the required params orig_contact_id to be replaced'); | |
79 | } | |
80 | if (!empty($params['orig_contact_id']) && !in_array($params['orig_contact_id'], $origContactIds)) { | |
81 | throw new API_Exception('Invalid case contact id (orig_contact_id)'); | |
82 | } | |
83 | $origContactId = $params['orig_contact_id']; | |
2a3c0d28 | 84 | } |
ea2aa8ff MW |
85 | else { |
86 | // Only one original contact ID | |
87 | $origContactId = CRM_Utils_Array::first($origContactIds); | |
da4d8910 | 88 | } |
6a488035 | 89 | |
ea2aa8ff MW |
90 | // Get the specified main contact ID for the case |
91 | $mainContactId = CRM_Utils_Array::first($params['contact_id']); | |
da4d8910 | 92 | |
ea2aa8ff MW |
93 | // If the main contact ID is not in the list of original contact IDs for the case we need to change the main contact ID for the case |
94 | // This means we'll end up with a new case ID | |
95 | if (!in_array($mainContactId, $origContactIds)) { | |
96 | $mergedCaseIds = CRM_Case_BAO_Case::mergeCases($mainContactId, $params['id'], $origContactId, NULL, TRUE); | |
97 | // If we merged cases then the first element will contain the case ID of the merged case - update that one | |
98 | $params['id'] = CRM_Utils_Array::first($mergedCaseIds); | |
99 | } | |
da4d8910 MW |
100 | } |
101 | } | |
102 | ||
103 | // Create/update the case | |
104 | $caseBAO = CRM_Case_BAO_Case::create($params); | |
6a488035 TO |
105 | |
106 | if (!$caseBAO) { | |
784e85a1 | 107 | throw new API_Exception('Case not created. Please check input params.'); |
6a488035 TO |
108 | } |
109 | ||
6e36c320 | 110 | if (isset($params['contact_id']) && !isset($params['id'])) { |
791a8dc4 | 111 | foreach ((array) $params['contact_id'] as $cid) { |
cf8f0fff | 112 | $contactParams = ['case_id' => $caseBAO->id, 'contact_id' => $cid]; |
791a8dc4 MW |
113 | CRM_Case_BAO_CaseContact::create($contactParams); |
114 | } | |
6a488035 TO |
115 | } |
116 | ||
da4d8910 | 117 | if (!isset($params['id'])) { |
2a3c0d28 MW |
118 | // As the API was not passed an id we have created a new case. |
119 | // Only run the xmlProcessor for new cases to get all configuration for the new case. | |
da4d8910 MW |
120 | _civicrm_api3_case_create_xmlProcessor($params, $caseBAO); |
121 | } | |
122 | ||
123 | // return case | |
cf8f0fff | 124 | $values = []; |
da4d8910 MW |
125 | _civicrm_api3_object_to_array($caseBAO, $values[$caseBAO->id]); |
126 | ||
da4d8910 MW |
127 | return civicrm_api3_create_success($values, $params, 'Case', 'create', $caseBAO); |
128 | } | |
129 | ||
2a3c0d28 MW |
130 | /** |
131 | * When creating a new case, run the xmlProcessor to get all necessary params/configuration | |
132 | * for the new case, as cases use an xml file to store their configuration. | |
ea2aa8ff | 133 | * |
2a3c0d28 MW |
134 | * @param $params |
135 | * @param $caseBAO | |
ea2aa8ff MW |
136 | * |
137 | * @throws \Exception | |
2a3c0d28 | 138 | */ |
da4d8910 MW |
139 | function _civicrm_api3_case_create_xmlProcessor($params, $caseBAO) { |
140 | // Format params for xmlProcessor | |
ebf4aeaa MW |
141 | if (isset($caseBAO->id)) { |
142 | $params['id'] = $caseBAO->id; | |
143 | } | |
da4d8910 | 144 | |
6a488035 TO |
145 | // Initialize XML processor with $params |
146 | $xmlProcessor = new CRM_Case_XMLProcessor_Process(); | |
cf8f0fff | 147 | $xmlProcessorParams = [ |
6b409353 CW |
148 | 'clientID' => $params['contact_id'] ?? NULL, |
149 | 'creatorID' => $params['creator_id'] ?? NULL, | |
6a488035 TO |
150 | 'standardTimeline' => 1, |
151 | 'activityTypeName' => 'Open Case', | |
6b409353 CW |
152 | 'caseID' => $params['id'] ?? NULL, |
153 | 'subject' => $params['subject'] ?? NULL, | |
154 | 'location' => $params['location'] ?? NULL, | |
155 | 'activity_date_time' => $params['start_date'] ?? NULL, | |
156 | 'duration' => $params['duration'] ?? NULL, | |
157 | 'medium_id' => $params['medium_id'] ?? NULL, | |
158 | 'details' => $params['details'] ?? NULL, | |
cf8f0fff | 159 | 'custom' => [], |
6b409353 | 160 | 'relationship_end_date' => $params['end_date'] ?? NULL, |
cf8f0fff | 161 | ]; |
6a488035 TO |
162 | |
163 | // Do it! :-D | |
164 | $xmlProcessor->run($params['case_type'], $xmlProcessorParams); | |
6a488035 TO |
165 | } |
166 | ||
11e09c59 | 167 | /** |
2fb1dd66 | 168 | * Adjust Metadata for Get Action. |
6a488035 | 169 | * |
cf470720 | 170 | * @param array $params |
2fb1dd66 | 171 | * Parameters determined by getfields. |
6a488035 TO |
172 | */ |
173 | function _civicrm_api3_case_get_spec(&$params) { | |
cf8f0fff CW |
174 | $params['contact_id'] = [ |
175 | 'api.aliases' => ['client_id'], | |
d142432b | 176 | 'title' => 'Case Client', |
48bdb3cd | 177 | 'description' => 'Contact id of one or more clients to retrieve cases for', |
d142432b | 178 | 'type' => CRM_Utils_Type::T_INT, |
cf8f0fff CW |
179 | ]; |
180 | $params['activity_id'] = [ | |
48bdb3cd CW |
181 | 'title' => 'Case Activity', |
182 | 'description' => 'Id of an activity in the case', | |
183 | 'type' => CRM_Utils_Type::T_INT, | |
cf8f0fff CW |
184 | ]; |
185 | $params['tag_id'] = [ | |
9ef16723 | 186 | 'title' => 'Tags', |
cde84d6f | 187 | 'description' => 'Find cases with specified tags.', |
9ef16723 CW |
188 | 'type' => 1, |
189 | 'FKClassName' => 'CRM_Core_DAO_Tag', | |
190 | 'FKApiName' => 'Tag', | |
191 | 'supports_joins' => TRUE, | |
cf8f0fff | 192 | ]; |
6a488035 TO |
193 | } |
194 | ||
11e09c59 | 195 | /** |
2fb1dd66 | 196 | * Adjust Metadata for Create Action. |
6a488035 | 197 | * |
cf470720 | 198 | * @param array $params |
b081365f | 199 | * Array of parameters determined by getfields. |
6a488035 TO |
200 | */ |
201 | function _civicrm_api3_case_create_spec(&$params) { | |
cf8f0fff CW |
202 | $params['contact_id'] = [ |
203 | 'api.aliases' => ['client_id'], | |
d142432b | 204 | 'title' => 'Case Client', |
48bdb3cd | 205 | 'description' => 'Contact id of case client(s)', |
d142432b EM |
206 | 'api.required' => 1, |
207 | 'type' => CRM_Utils_Type::T_INT, | |
6e36c320 | 208 | 'FKApiName' => 'Contact', |
cf8f0fff | 209 | ]; |
6a488035 | 210 | $params['status_id']['api.default'] = 1; |
cf8f0fff | 211 | $params['status_id']['api.aliases'] = ['case_status']; |
941feb14 EM |
212 | $params['creator_id']['api.default'] = 'user_contact_id'; |
213 | $params['creator_id']['type'] = CRM_Utils_Type::T_INT; | |
b05e6d0d | 214 | $params['creator_id']['title'] = 'Case Created By'; |
ca11b9da | 215 | $params['start_date']['api.default'] = 'now'; |
cf8f0fff | 216 | $params['medium_id'] = [ |
16c0ec8d CW |
217 | 'name' => 'medium_id', |
218 | 'title' => 'Activity Medium', | |
d142432b | 219 | 'type' => CRM_Utils_Type::T_INT, |
cf8f0fff | 220 | ]; |
6a488035 TO |
221 | } |
222 | ||
11e09c59 | 223 | /** |
2fb1dd66 | 224 | * Adjust Metadata for Update action. |
6a488035 | 225 | * |
cf470720 | 226 | * @param array $params |
b081365f | 227 | * Array of parameters determined by getfields. |
6a488035 TO |
228 | */ |
229 | function _civicrm_api3_case_update_spec(&$params) { | |
230 | $params['id']['api.required'] = 1; | |
231 | } | |
232 | ||
11e09c59 | 233 | /** |
c1a920f1 | 234 | * Adjust Metadata for Delete action. |
6a488035 | 235 | * |
cf470720 | 236 | * @param array $params |
b081365f | 237 | * Array of parameters determined by getfields. |
6a488035 TO |
238 | */ |
239 | function _civicrm_api3_case_delete_spec(&$params) { | |
240 | $params['id']['api.required'] = 1; | |
241 | } | |
242 | ||
6a488035 | 243 | /** |
211e2fca | 244 | * Get details of a particular case, or search for cases, depending on params. |
6a488035 TO |
245 | * |
246 | * Please provide one (and only one) of the four get/search parameters: | |
247 | * | |
6c552737 TO |
248 | * @param array $params |
249 | * 'id' => if set, will get all available info about a case, including contacts and activities | |
6a488035 | 250 | * |
6c552737 TO |
251 | * // if no case_id provided, this function will use one of the following search parameters: |
252 | * 'client_id' => finds all cases with a specific client | |
253 | * 'activity_id' => returns the case containing a specific activity | |
254 | * 'contact_id' => finds all cases associated with a contact (in any role, not just client) | |
09f6c512 CW |
255 | * $params CRM_Utils_SQL_Select $sql |
256 | * Other apis wishing to wrap & extend this one can pass in a $sql object with extra clauses | |
6a488035 | 257 | * |
77b97be7 | 258 | * @throws API_Exception |
a6c01b45 | 259 | * @return array |
72b3a70c | 260 | * (get mode, case_id provided): Array with case details, case roles, case activity ids, (search mode, case_id not provided): Array of cases found |
6a488035 | 261 | */ |
09f6c512 | 262 | function civicrm_api3_case_get($params, $sql = NULL) { |
6a488035 | 263 | $options = _civicrm_api3_get_options_from_params($params); |
09f6c512 CW |
264 | if (!is_a($sql, 'CRM_Utils_SQL_Select')) { |
265 | $sql = CRM_Utils_SQL_Select::fragment(); | |
266 | } | |
48bdb3cd CW |
267 | |
268 | // Add clause to search by client | |
6a488035 | 269 | if (!empty($params['contact_id'])) { |
9808e205 CW |
270 | // Legacy support - this field historically supports a nonstandard format of array(1,2,3) as a synonym for array('IN' => array(1,2,3)) |
271 | if (is_array($params['contact_id'])) { | |
272 | $operator = CRM_Utils_Array::first(array_keys($params['contact_id'])); | |
273 | if (!in_array($operator, \CRM_Core_DAO::acceptedSQLOperators(), TRUE)) { | |
cf8f0fff | 274 | $params['contact_id'] = ['IN' => $params['contact_id']]; |
6a488035 TO |
275 | } |
276 | } | |
9808e205 | 277 | else { |
cf8f0fff | 278 | $params['contact_id'] = ['=' => $params['contact_id']]; |
9808e205 CW |
279 | } |
280 | $clause = CRM_Core_DAO::createSQLFilter('contact_id', $params['contact_id']); | |
281 | $sql->where("a.id IN (SELECT case_id FROM civicrm_case_contact WHERE $clause)"); | |
6a488035 TO |
282 | } |
283 | ||
242220e2 CW |
284 | // Order by case contact (primary client) |
285 | // Ex: "contact_id", "contact_id.display_name", "contact_id.sort_name DESC". | |
286 | if (!empty($options['sort']) && strpos($options['sort'], 'contact_id') !== FALSE) { | |
287 | $sort = explode(', ', $options['sort']); | |
288 | $contactSort = NULL; | |
4c6cc364 | 289 | foreach ($sort as $index => &$sortString) { |
242220e2 CW |
290 | if (strpos($sortString, 'contact_id') === 0) { |
291 | $contactSort = $sortString; | |
4c6cc364 | 292 | $sortString = '(1)'; |
242220e2 CW |
293 | // Get sort field and direction |
294 | list($sortField, $dir) = array_pad(explode(' ', $contactSort), 2, 'ASC'); | |
295 | list(, $sortField) = array_pad(explode('.', $sortField), 2, 'id'); | |
296 | // Validate inputs | |
297 | if (!array_key_exists($sortField, CRM_Contact_DAO_Contact::fieldKeys()) || ($dir != 'ASC' && $dir != 'DESC')) { | |
298 | throw new API_Exception("Unknown field specified for sort. Cannot order by '$contactSort'"); | |
299 | } | |
4c6cc364 | 300 | $sql->orderBy("case_contact.$sortField $dir", NULL, $index); |
242220e2 CW |
301 | } |
302 | } | |
303 | // Remove contact sort params so the basic_get function doesn't see them | |
304 | $params['options']['sort'] = implode(', ', $sort); | |
305 | unset($params['option_sort'], $params['option.sort'], $params['sort']); | |
306 | // Add necessary joins to the first case client | |
307 | if ($contactSort) { | |
308 | $sql->join('ccc', 'LEFT JOIN (SELECT * FROM civicrm_case_contact WHERE id IN (SELECT MIN(id) FROM civicrm_case_contact GROUP BY case_id)) AS ccc ON ccc.case_id = a.id'); | |
7a51786d | 309 | $sql->join('case_contact', 'LEFT JOIN civicrm_contact AS case_contact ON ccc.contact_id = case_contact.id AND case_contact.is_deleted <> 1'); |
242220e2 CW |
310 | } |
311 | } | |
312 | ||
48bdb3cd | 313 | // Add clause to search by activity |
6a488035 | 314 | if (!empty($params['activity_id'])) { |
48bdb3cd | 315 | if (!CRM_Utils_Rule::positiveInteger($params['activity_id'])) { |
784e85a1 | 316 | throw new API_Exception('Invalid parameter: activity_id. Must provide a numeric value.'); |
6a488035 | 317 | } |
48bdb3cd CW |
318 | $activityId = $params['activity_id']; |
319 | $originalId = CRM_Core_DAO::getFieldValue('CRM_Activity_BAO_Activity', $activityId, 'original_id'); | |
320 | if ($originalId) { | |
321 | $activityId .= ',' . $originalId; | |
6a488035 | 322 | } |
48bdb3cd CW |
323 | $sql |
324 | ->join('civicrm_case_activity', 'INNER JOIN civicrm_case_activity ON civicrm_case_activity.case_id = a.id') | |
325 | ->where("civicrm_case_activity.activity_id IN ($activityId)"); | |
6a488035 | 326 | } |
f21557af | 327 | |
9ef16723 CW |
328 | // Clause to search by tag |
329 | if (!empty($params['tag_id'])) { | |
cf8f0fff | 330 | $dummySpec = []; |
9ef16723 CW |
331 | _civicrm_api3_validate_integer($params, 'tag_id', $dummySpec, 'Case'); |
332 | if (!is_array($params['tag_id'])) { | |
cf8f0fff | 333 | $params['tag_id'] = ['=' => $params['tag_id']]; |
9ef16723 CW |
334 | } |
335 | $clause = \CRM_Core_DAO::createSQLFilter('tag_id', $params['tag_id']); | |
336 | if ($clause) { | |
cf8f0fff | 337 | $sql->where('a.id IN (SELECT entity_id FROM civicrm_entity_tag WHERE entity_table = "civicrm_case" AND !clause)', ['!clause' => $clause]); |
9ef16723 CW |
338 | } |
339 | } | |
340 | ||
cf8f0fff | 341 | $cases = _civicrm_api3_basic_get(_civicrm_api3_get_BAO(__FUNCTION__), ['sequential' => 0] + $params, TRUE, 'Case', $sql); |
a54ac083 | 342 | |
9ef16723 | 343 | if (empty($options['is_count']) && !empty($cases['values'])) { |
abd06efc | 344 | // For historic reasons we return these by default only when fetching a case by id |
10befc1f | 345 | if (!empty($params['id']) && is_numeric($params['id']) && empty($options['return'])) { |
cf8f0fff | 346 | $options['return'] = [ |
abd06efc CW |
347 | 'contacts' => 1, |
348 | 'activities' => 1, | |
349 | 'contact_id' => 1, | |
cf8f0fff | 350 | ]; |
abd06efc | 351 | } |
e01eccc0 | 352 | |
9ef16723 CW |
353 | _civicrm_api3_case_read($cases['values'], $options); |
354 | ||
355 | // We disabled sequential to keep the list indexed for case_read(). Now add it back. | |
356 | if (!empty($params['sequential'])) { | |
357 | $cases['values'] = array_values($cases['values']); | |
abd06efc | 358 | } |
35671d00 | 359 | } |
f21557af | 360 | |
9ef16723 | 361 | return $cases; |
6a488035 TO |
362 | } |
363 | ||
364 | /** | |
35823763 EM |
365 | * Deprecated API. |
366 | * | |
367 | * Use activity API instead. | |
9657ccf2 EM |
368 | * |
369 | * @param array $params | |
370 | * | |
371 | * @throws API_Exception | |
372 | * @return array | |
6a488035 TO |
373 | */ |
374 | function civicrm_api3_case_activity_create($params) { | |
a14e9d08 | 375 | require_once "api/v3/Activity.php"; |
cf8f0fff | 376 | return civicrm_api3_activity_create($params) + [ |
a14e9d08 | 377 | 'deprecated' => CRM_Utils_Array::value('activity_create', _civicrm_api3_case_deprecation()), |
cf8f0fff | 378 | ]; |
ae76ce5e CW |
379 | } |
380 | ||
381 | /** | |
382 | * Add a timeline to a case. | |
383 | * | |
384 | * @param array $params | |
385 | * | |
386 | * @throws API_Exception | |
387 | * @return array | |
388 | */ | |
389 | function civicrm_api3_case_addtimeline($params) { | |
390 | $caseType = CRM_Case_BAO_Case::getCaseType($params['case_id'], 'name'); | |
391 | $xmlProcessor = new CRM_Case_XMLProcessor_Process(); | |
cf8f0fff | 392 | $xmlProcessorParams = [ |
ae76ce5e CW |
393 | 'clientID' => CRM_Case_BAO_Case::getCaseClients($params['case_id']), |
394 | 'creatorID' => $params['creator_id'], | |
395 | 'standardTimeline' => 0, | |
396 | 'activity_date_time' => $params['activity_date_time'], | |
397 | 'caseID' => $params['case_id'], | |
398 | 'caseType' => $caseType, | |
399 | 'activitySetName' => $params['timeline'], | |
cf8f0fff | 400 | ]; |
ae76ce5e CW |
401 | $xmlProcessor->run($caseType, $xmlProcessorParams); |
402 | return civicrm_api3_create_success(); | |
403 | } | |
404 | ||
405 | /** | |
406 | * Adjust Metadata for addtimeline action. | |
407 | * | |
408 | * @param array $params | |
409 | * Array of parameters determined by getfields. | |
410 | */ | |
411 | function _civicrm_api3_case_addtimeline_spec(&$params) { | |
cf8f0fff | 412 | $params['case_id'] = [ |
ae76ce5e CW |
413 | 'title' => 'Case ID', |
414 | 'description' => 'Id of case to update', | |
415 | 'type' => CRM_Utils_Type::T_INT, | |
416 | 'api.required' => 1, | |
cf8f0fff CW |
417 | ]; |
418 | $params['timeline'] = [ | |
ae76ce5e CW |
419 | 'title' => 'Timeline', |
420 | 'description' => 'Name of activity set', | |
421 | 'type' => CRM_Utils_Type::T_STRING, | |
422 | 'api.required' => 1, | |
cf8f0fff CW |
423 | ]; |
424 | $params['activity_date_time'] = [ | |
ae76ce5e CW |
425 | 'api.default' => 'now', |
426 | 'title' => 'Activity date time', | |
427 | 'description' => 'Timeline start date', | |
428 | 'type' => CRM_Utils_Type::T_DATE, | |
cf8f0fff CW |
429 | ]; |
430 | $params['creator_id'] = [ | |
ae76ce5e CW |
431 | 'api.default' => 'user_contact_id', |
432 | 'title' => 'Activity creator', | |
433 | 'description' => 'Contact id of timeline creator', | |
434 | 'type' => CRM_Utils_Type::T_INT, | |
cf8f0fff | 435 | ]; |
a14e9d08 CW |
436 | } |
437 | ||
a6bc7218 CW |
438 | /** |
439 | * Merge 2 cases. | |
440 | * | |
441 | * @param array $params | |
442 | * | |
443 | * @throws API_Exception | |
444 | * @return array | |
445 | */ | |
446 | function civicrm_api3_case_merge($params) { | |
447 | $clients1 = CRM_Case_BAO_Case::getCaseClients($params['case_id_1']); | |
448 | $clients2 = CRM_Case_BAO_Case::getCaseClients($params['case_id_2']); | |
449 | CRM_Case_BAO_Case::mergeCases($clients1[0], $params['case_id_1'], $clients2[0], $params['case_id_2']); | |
450 | return civicrm_api3_create_success(); | |
451 | } | |
452 | ||
453 | /** | |
454 | * Adjust Metadata for merge action. | |
455 | * | |
456 | * @param array $params | |
457 | * Array of parameters determined by getfields. | |
458 | */ | |
459 | function _civicrm_api3_case_merge_spec(&$params) { | |
cf8f0fff | 460 | $params['case_id_1'] = [ |
a6bc7218 CW |
461 | 'title' => 'Case ID 1', |
462 | 'description' => 'Id of main case', | |
463 | 'type' => CRM_Utils_Type::T_INT, | |
464 | 'api.required' => 1, | |
cf8f0fff CW |
465 | ]; |
466 | $params['case_id_2'] = [ | |
a6bc7218 CW |
467 | 'title' => 'Case ID 2', |
468 | 'description' => 'Id of second case', | |
469 | 'type' => CRM_Utils_Type::T_INT, | |
470 | 'api.required' => 1, | |
cf8f0fff | 471 | ]; |
a6bc7218 CW |
472 | } |
473 | ||
a14e9d08 | 474 | /** |
35823763 EM |
475 | * Declare deprecated api functions. |
476 | * | |
a14e9d08 | 477 | * @deprecated api notice |
a6c01b45 | 478 | * @return array |
16b10e64 | 479 | * Array of deprecated actions |
a14e9d08 CW |
480 | */ |
481 | function _civicrm_api3_case_deprecation() { | |
cf8f0fff | 482 | return ['activity_create' => 'Case api "activity_create" action is deprecated. Use the activity api instead.']; |
6a488035 TO |
483 | } |
484 | ||
485 | /** | |
3755d879 | 486 | * @deprecated Update a specified case. Use civicrm_api3_case_create() instead. |
6a488035 | 487 | * |
6c552737 TO |
488 | * @param array $params |
489 | * //REQUIRED: | |
490 | * 'case_id' => int | |
6a488035 | 491 | * |
6c552737 TO |
492 | * //OPTIONAL |
493 | * 'status_id' => int | |
494 | * 'start_date' => str datestamp | |
495 | * 'contact_id' => int // case client | |
6a488035 | 496 | * |
784e85a1 | 497 | * @throws API_Exception |
a6c01b45 | 498 | * @return array |
72b3a70c | 499 | * api result array |
6a488035 TO |
500 | */ |
501 | function civicrm_api3_case_update($params) { | |
e720ce6e CB |
502 | if (!isset($params['case_id']) && isset($params['id'])) { |
503 | $params['case_id'] = $params['id']; | |
504 | } | |
505 | ||
6a488035 | 506 | //check parameters |
cf8f0fff | 507 | civicrm_api3_verify_mandatory($params, NULL, ['id']); |
6a488035 | 508 | |
784e85a1 | 509 | // return error if modifying creator id |
6a488035 | 510 | if (array_key_exists('creator_id', $params)) { |
784e85a1 | 511 | throw new API_Exception(ts('You cannot update creator id')); |
6a488035 TO |
512 | } |
513 | ||
cf8f0fff | 514 | $mCaseId = $origContactIds = []; |
6a488035 TO |
515 | |
516 | // get original contact id and creator id of case | |
517 | if (!empty($params['contact_id'])) { | |
c2e81506 | 518 | $origContactIds = CRM_Case_BAO_Case::retrieveContactIdsByCaseId($params['id']); |
f37c1b47 | 519 | $origContactId = CRM_Utils_Array::first($origContactIds); |
6a488035 TO |
520 | } |
521 | ||
522 | if (count($origContactIds) > 1) { | |
523 | // check valid orig contact id | |
524 | if (!empty($params['orig_contact_id']) && !in_array($params['orig_contact_id'], $origContactIds)) { | |
784e85a1 | 525 | throw new API_Exception('Invalid case contact id (orig_contact_id)'); |
6a488035 TO |
526 | } |
527 | elseif (empty($params['orig_contact_id'])) { | |
784e85a1 | 528 | throw new API_Exception('Case is linked with more than one contact id. Provide the required params orig_contact_id to be replaced'); |
6a488035 TO |
529 | } |
530 | $origContactId = $params['orig_contact_id']; | |
531 | } | |
532 | ||
533 | // check for same contact id for edit Client | |
534 | if (!empty($params['contact_id']) && !in_array($params['contact_id'], $origContactIds)) { | |
535 | $mCaseId = CRM_Case_BAO_Case::mergeCases($params['contact_id'], $params['case_id'], $origContactId, NULL, TRUE); | |
536 | } | |
537 | ||
538 | if (!empty($mCaseId[0])) { | |
c2e81506 | 539 | $params['id'] = $mCaseId[0]; |
6a488035 TO |
540 | } |
541 | ||
542 | $dao = new CRM_Case_BAO_Case(); | |
543 | $dao->id = $params['id']; | |
544 | ||
545 | $dao->copyValues($params); | |
546 | $dao->save(); | |
547 | ||
cf8f0fff | 548 | $case = []; |
6a488035 TO |
549 | _civicrm_api3_object_to_array($dao, $case); |
550 | ||
cf8f0fff | 551 | return civicrm_api3_create_success([$dao->id => $case], $params, 'Case', 'update', $dao); |
6a488035 TO |
552 | } |
553 | ||
554 | /** | |
555 | * Delete a specified case. | |
556 | * | |
6c552737 | 557 | * @param array $params |
37b8953e | 558 | * |
0b882a86 | 559 | * ``` |
6c552737 TO |
560 | * //REQUIRED: |
561 | * 'id' => int | |
6a488035 | 562 | * |
6c552737 TO |
563 | * //OPTIONAL |
564 | * 'move_to_trash' => bool (defaults to false) | |
0b882a86 | 565 | * ``` |
6a488035 | 566 | * |
77b97be7 | 567 | * @throws API_Exception |
8572e6de | 568 | * @return mixed |
6a488035 TO |
569 | */ |
570 | function civicrm_api3_case_delete($params) { | |
571 | //check parameters | |
cf8f0fff | 572 | civicrm_api3_verify_mandatory($params, NULL, ['id']); |
6a488035 TO |
573 | |
574 | if (CRM_Case_BAO_Case::deleteCase($params['id'], CRM_Utils_Array::value('move_to_trash', $params, FALSE))) { | |
244bbdd8 | 575 | return civicrm_api3_create_success($params, $params, 'Case', 'delete'); |
6a488035 TO |
576 | } |
577 | else { | |
784e85a1 | 578 | throw new API_Exception('Could not delete case.'); |
6a488035 TO |
579 | } |
580 | } | |
581 | ||
8572e6de CW |
582 | /** |
583 | * Case.restore API specification | |
584 | * | |
585 | * @param array $spec description of fields supported by this API call | |
586 | * @return void | |
587 | */ | |
588 | function _civicrm_api3_case_restore_spec(&$spec) { | |
cf8f0fff CW |
589 | $result = civicrm_api3('Case', 'getfields', ['api_action' => 'delete']); |
590 | $spec = ['id' => $result['values']['id']]; | |
8572e6de CW |
591 | } |
592 | ||
593 | /** | |
594 | * Restore a specified case from the trash. | |
595 | * | |
596 | * @param array $params | |
597 | * @throws API_Exception | |
598 | * @return mixed | |
599 | */ | |
600 | function civicrm_api3_case_restore($params) { | |
601 | if (CRM_Case_BAO_Case::restoreCase($params['id'])) { | |
602 | return civicrm_api3_create_success($params, $params, 'Case', 'restore'); | |
603 | } | |
604 | else { | |
605 | throw new API_Exception('Could not restore case.'); | |
606 | } | |
607 | } | |
608 | ||
6a488035 | 609 | /** |
9ef16723 | 610 | * Augment case results with extra data. |
6a488035 | 611 | * |
9ef16723 | 612 | * @param array $cases |
e9ff5391 | 613 | * @param array $options |
6a488035 | 614 | */ |
9ef16723 CW |
615 | function _civicrm_api3_case_read(&$cases, $options) { |
616 | foreach ($cases as &$case) { | |
617 | if (empty($options['return']) || !empty($options['return']['contact_id'])) { | |
618 | // Legacy support for client_id - TODO: in apiv4 remove 'client_id' | |
ea2aa8ff MW |
619 | // FIXME: Historically we return a 1-based array. Changing that risks breaking API clients that |
620 | // have been hardcoded to index "1", instead of the first array index (eg. using reset(), foreach etc) | |
621 | $case['client_id'] = $case['contact_id'] = CRM_Case_BAO_Case::retrieveContactIdsByCaseId($case['id'], NULL, 1); | |
9ef16723 CW |
622 | } |
623 | if (!empty($options['return']['contacts'])) { | |
624 | //get case contacts | |
625 | $contacts = CRM_Case_BAO_Case::getcontactNames($case['id']); | |
626 | $relations = CRM_Case_BAO_Case::getRelatedContacts($case['id']); | |
627 | $case['contacts'] = array_unique(array_merge($contacts, $relations), SORT_REGULAR); | |
628 | } | |
629 | if (!empty($options['return']['activities'])) { | |
630 | // add case activities array - we'll populate them in bulk below | |
cf8f0fff | 631 | $case['activities'] = []; |
9ef16723 CW |
632 | } |
633 | // Properly render this joined field | |
634 | if (!empty($options['return']['case_type_id.definition'])) { | |
635 | if (!empty($case['case_type_id.definition'])) { | |
636 | list($xml) = CRM_Utils_XML::parseString($case['case_type_id.definition']); | |
637 | } | |
638 | else { | |
639 | $caseTypeId = !empty($case['case_type_id']) ? $case['case_type_id'] : CRM_Core_DAO::getFieldValue('CRM_Case_DAO_Case', $case['id'], 'case_type_id'); | |
640 | $caseTypeName = !empty($case['case_type_id.name']) ? $case['case_type_id.name'] : CRM_Core_DAO::getFieldValue('CRM_Case_DAO_CaseType', $caseTypeId, 'name'); | |
641 | $xml = CRM_Case_XMLRepository::singleton()->retrieve($caseTypeName); | |
642 | } | |
cf8f0fff | 643 | $case['case_type_id.definition'] = []; |
9ef16723 CW |
644 | if ($xml) { |
645 | $case['case_type_id.definition'] = CRM_Case_BAO_CaseType::convertXmlToDefinition($xml); | |
646 | } | |
647 | } | |
a54ac083 | 648 | } |
9ef16723 | 649 | // Bulk-load activities |
a54ac083 | 650 | if (!empty($options['return']['activities'])) { |
9ef16723 | 651 | $query = "SELECT case_id, activity_id FROM civicrm_case_activity WHERE case_id IN (%1)"; |
cf8f0fff | 652 | $params = [1 => [implode(',', array_keys($cases)), 'String', CRM_Core_DAO::QUERY_FORMAT_NO_QUOTES]]; |
9ef16723 | 653 | $dao = CRM_Core_DAO::executeQuery($query, $params); |
a54ac083 | 654 | while ($dao->fetch()) { |
9ef16723 | 655 | $cases[$dao->case_id]['activities'][] = $dao->activity_id; |
6a488035 | 656 | } |
6a488035 | 657 | } |
9ef16723 | 658 | // Bulk-load tags. Supports joins onto the tag entity. |
cf8f0fff | 659 | $tagGet = ['tag_id', 'entity_id']; |
9ef16723 CW |
660 | foreach (array_keys($options['return']) as $key) { |
661 | if (strpos($key, 'tag_id.') === 0) { | |
662 | $tagGet[] = $key; | |
663 | $options['return']['tag_id'] = 1; | |
69f9c562 | 664 | } |
9ef16723 CW |
665 | } |
666 | if (!empty($options['return']['tag_id'])) { | |
cf8f0fff | 667 | $tags = civicrm_api3('EntityTag', 'get', [ |
9ef16723 | 668 | 'entity_table' => 'civicrm_case', |
cf8f0fff | 669 | 'entity_id' => ['IN' => array_keys($cases)], |
9ef16723 | 670 | 'return' => $tagGet, |
cf8f0fff CW |
671 | 'options' => ['limit' => 0], |
672 | ]); | |
9ef16723 CW |
673 | foreach ($tags['values'] as $tag) { |
674 | $key = (int) $tag['entity_id']; | |
675 | unset($tag['entity_id'], $tag['id']); | |
676 | $cases[$key]['tag_id'][$tag['tag_id']] = $tag; | |
69f9c562 CW |
677 | } |
678 | } | |
6a488035 TO |
679 | } |
680 | ||
681 | /** | |
61fe4988 | 682 | * Internal function to format create params for processing. |
9657ccf2 EM |
683 | * |
684 | * @param array $params | |
6a488035 TO |
685 | */ |
686 | function _civicrm_api3_case_format_params(&$params) { | |
2a3c0d28 | 687 | // Format/include custom params |
cf8f0fff | 688 | $values = []; |
2a3c0d28 MW |
689 | _civicrm_api3_custom_format_params($params, $values, 'Case'); |
690 | $params = array_merge($params, $values); | |
ebf4aeaa | 691 | |
ea2aa8ff MW |
692 | // A single or multiple contact_id (client_id) can be passed as a value or array. |
693 | // Convert single value to array here to simplify processing in later functions which expect an array. | |
694 | if (isset($params['contact_id'])) { | |
695 | if (!is_array($params['contact_id'])) { | |
cf8f0fff | 696 | $params['contact_id'] = [$params['contact_id']]; |
ea2aa8ff MW |
697 | } |
698 | } | |
699 | ||
700 | // DEPRECATED: case_id - use id parameter instead. | |
701 | if (!isset($params['id']) && isset($params['case_id'])) { | |
702 | $params['id'] = $params['case_id']; | |
703 | } | |
704 | ||
705 | // When creating a new case, either case_type_id or case_type must be specified. | |
2a3c0d28 | 706 | if (empty($params['case_type_id']) && empty($params['case_type'])) { |
ea2aa8ff | 707 | // If both case_type_id and case_type are empty we are updating a case so return here. |
2a3c0d28 MW |
708 | return; |
709 | } | |
710 | ||
ea2aa8ff | 711 | // We are creating a new case |
2a3c0d28 MW |
712 | // figure out case_type_id from case_type and vice-versa |
713 | $caseTypes = CRM_Case_PseudoConstant::caseType('name', FALSE); | |
714 | if (empty($params['case_type_id'])) { | |
715 | $params['case_type_id'] = array_search($params['case_type'], $caseTypes); | |
716 | ||
717 | // DEPRECATED: lookup by label for backward compatibility | |
718 | if (!$params['case_type_id']) { | |
719 | $caseTypeLabels = CRM_Case_PseudoConstant::caseType('title', FALSE); | |
720 | $params['case_type_id'] = array_search($params['case_type'], $caseTypeLabels); | |
61fe4988 | 721 | $params['case_type'] = $caseTypes[$params['case_type_id']]; |
3f25e694 | 722 | } |
6a488035 | 723 | } |
2a3c0d28 MW |
724 | elseif (empty($params['case_type'])) { |
725 | $params['case_type'] = $caseTypes[$params['case_type_id']]; | |
726 | } | |
6a488035 | 727 | } |
ff9340a4 CW |
728 | |
729 | /** | |
730 | * It actually works a lot better to use the CaseContact api instead of the Case api | |
731 | * for entityRef fields so we can perform the necessary joins, | |
732 | * so we pass off getlist requests to the CaseContact api. | |
733 | * | |
734 | * @param array $params | |
735 | * @return mixed | |
736 | */ | |
737 | function civicrm_api3_case_getList($params) { | |
738 | require_once 'api/v3/Generic/Getlist.php'; | |
739 | require_once 'api/v3/CaseContact.php'; | |
09d55aa3 | 740 | //CRM:19956 - Assign case_id param if both id and case_id is passed to retrieve the case |
741 | if (!empty($params['id']) && !empty($params['params']) && !empty($params['params']['case_id'])) { | |
cf8f0fff | 742 | $params['params']['case_id'] = ['IN' => $params['id']]; |
09d55aa3 | 743 | unset($params['id']); |
744 | } | |
ff9340a4 CW |
745 | $params['id_field'] = 'case_id'; |
746 | $params['label_field'] = $params['search_field'] = 'contact_id.sort_name'; | |
cf8f0fff | 747 | $params['description_field'] = [ |
ff9340a4 CW |
748 | 'case_id', |
749 | 'case_id.case_type_id.title', | |
750 | 'case_id.subject', | |
751 | 'case_id.status_id', | |
752 | 'case_id.start_date', | |
cf8f0fff CW |
753 | ]; |
754 | $apiRequest = [ | |
09d55aa3 | 755 | 'version' => 3, |
ff9340a4 CW |
756 | 'entity' => 'CaseContact', |
757 | 'action' => 'getlist', | |
758 | 'params' => $params, | |
cf8f0fff | 759 | ]; |
ff9340a4 CW |
760 | return civicrm_api3_generic_getList($apiRequest); |
761 | } | |
762 | ||
763 | /** | |
764 | * Needed due to the above override | |
765 | * @param $params | |
766 | * @param $apiRequest | |
767 | */ | |
768 | function _civicrm_api3_case_getlist_spec(&$params, $apiRequest) { | |
769 | require_once 'api/v3/Generic/Getlist.php'; | |
770 | _civicrm_api3_generic_getlist_spec($params, $apiRequest); | |
771 | } |