Commit | Line | Data |
---|---|---|
6a488035 TO |
1 | <?php |
2 | /* | |
3 | +--------------------------------------------------------------------+ | |
7d61e75f | 4 | | Copyright CiviCRM LLC. All rights reserved. | |
6a488035 | 5 | | | |
7d61e75f 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 | 11 | |
6a488035 TO |
12 | /** |
13 | * Test APIv3 civicrm_report_instance_* functions | |
14 | * | |
6c6e6187 TO |
15 | * @package CiviCRM_APIv3 |
16 | * @subpackage API_Report | |
acb109b7 | 17 | * @group headless |
6a488035 | 18 | */ |
6a488035 | 19 | class api_v3_ReportTemplateTest extends CiviUnitTestCase { |
2af6f0c0 | 20 | |
21 | use CRMTraits_ACL_PermissionTrait; | |
e40ce31e | 22 | use CRMTraits_PCP_PCPTestTrait; |
d7a896b4 | 23 | use CRMTraits_Custom_CustomDataTrait; |
2af6f0c0 | 24 | |
5a14305b | 25 | protected $contactIDs = []; |
26 | ||
2c6b4783 | 27 | /** |
28 | * Our group reports use an alter so transaction cleanup won't work. | |
29 | * | |
30 | * @throws \Exception | |
31 | */ | |
32 | public function tearDown() { | |
33 | $this->quickCleanUpFinancialEntities(); | |
d7a896b4 | 34 | $this->quickCleanup(['civicrm_group', 'civicrm_saved_search', 'civicrm_group_contact', 'civicrm_group_contact_cache', 'civicrm_group'], TRUE); |
2c6b4783 | 35 | parent::tearDown(); |
6a488035 TO |
36 | } |
37 | ||
6a488035 | 38 | public function testReportTemplate() { |
9099cab3 | 39 | $result = $this->callAPISuccess('ReportTemplate', 'create', [ |
6a488035 TO |
40 | 'label' => 'Example Form', |
41 | 'description' => 'Longish description of the example form', | |
42 | 'class_name' => 'CRM_Report_Form_Examplez', | |
43 | 'report_url' => 'example/path', | |
44 | 'component' => 'CiviCase', | |
9099cab3 | 45 | ]); |
1cbea43e | 46 | $this->assertAPISuccess($result); |
ba4a1892 | 47 | $this->assertEquals(1, $result['count']); |
6a488035 | 48 | $entityId = $result['id']; |
ba4a1892 TM |
49 | $this->assertTrue(is_numeric($entityId)); |
50 | $this->assertEquals(7, $result['values'][$entityId]['component_id']); | |
6a488035 TO |
51 | $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value |
52 | WHERE name = "CRM_Report_Form_Examplez" | |
0201b57f | 53 | AND option_group_id IN (SELECT id from civicrm_option_group WHERE name = "report_template") '); |
6a488035 TO |
54 | $this->assertDBQuery(1, 'SELECT is_active FROM civicrm_option_value |
55 | WHERE name = "CRM_Report_Form_Examplez"'); | |
56 | ||
57 | // change component to null | |
9099cab3 | 58 | $result = $this->callAPISuccess('ReportTemplate', 'create', [ |
92915c55 | 59 | 'id' => $entityId, |
6a488035 | 60 | 'component' => '', |
9099cab3 | 61 | ]); |
1cbea43e | 62 | $this->assertAPISuccess($result); |
ba4a1892 | 63 | $this->assertEquals(1, $result['count']); |
6a488035 TO |
64 | $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value |
65 | WHERE name = "CRM_Report_Form_Examplez" | |
0201b57f | 66 | AND option_group_id IN (SELECT id from civicrm_option_group WHERE name = "report_template") '); |
6a488035 TO |
67 | $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value |
68 | WHERE name = "CRM_Report_Form_Examplez" | |
69 | AND component_id IS NULL'); | |
70 | ||
71 | // deactivate | |
9099cab3 | 72 | $result = $this->callAPISuccess('ReportTemplate', 'create', [ |
92915c55 | 73 | 'id' => $entityId, |
6a488035 | 74 | 'is_active' => 0, |
9099cab3 | 75 | ]); |
1cbea43e | 76 | $this->assertAPISuccess($result); |
ba4a1892 | 77 | $this->assertEquals(1, $result['count']); |
6a488035 TO |
78 | $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value |
79 | WHERE name = "CRM_Report_Form_Examplez" | |
0201b57f | 80 | AND option_group_id IN (SELECT id from civicrm_option_group WHERE name = "report_template") '); |
6a488035 TO |
81 | $this->assertDBQuery(0, 'SELECT is_active FROM civicrm_option_value |
82 | WHERE name = "CRM_Report_Form_Examplez"'); | |
83 | ||
84 | // activate | |
9099cab3 | 85 | $result = $this->callAPISuccess('ReportTemplate', 'create', [ |
92915c55 | 86 | 'id' => $entityId, |
6a488035 | 87 | 'is_active' => 1, |
9099cab3 | 88 | ]); |
1cbea43e | 89 | $this->assertAPISuccess($result); |
ba4a1892 | 90 | $this->assertEquals(1, $result['count']); |
6a488035 TO |
91 | $this->assertDBQuery(1, 'SELECT count(*) FROM civicrm_option_value |
92 | WHERE name = "CRM_Report_Form_Examplez" | |
0201b57f | 93 | AND option_group_id IN (SELECT id from civicrm_option_group WHERE name = "report_template") '); |
6a488035 TO |
94 | $this->assertDBQuery(1, 'SELECT is_active FROM civicrm_option_value |
95 | WHERE name = "CRM_Report_Form_Examplez"'); | |
96 | ||
9099cab3 | 97 | $result = $this->callAPISuccess('ReportTemplate', 'delete', [ |
92915c55 | 98 | 'id' => $entityId, |
9099cab3 | 99 | ]); |
1cbea43e | 100 | $this->assertAPISuccess($result); |
ba4a1892 | 101 | $this->assertEquals(1, $result['count']); |
6a488035 TO |
102 | $this->assertDBQuery(0, 'SELECT count(*) FROM civicrm_option_value |
103 | WHERE name = "CRM_Report_Form_Examplez" | |
104 | '); | |
105 | } | |
e2779f6e | 106 | |
880f81a1 | 107 | /** |
108 | * Test api to get rows from reports. | |
109 | * | |
110 | * @dataProvider getReportTemplatesSupportingSelectWhere | |
111 | * | |
112 | * @param $reportID | |
113 | * | |
a6439b6a | 114 | * @throws \PHPUnit\Framework\IncompleteTestError |
880f81a1 | 115 | */ |
116 | public function testReportTemplateSelectWhere($reportID) { | |
9099cab3 | 117 | $this->hookClass->setHook('civicrm_selectWhereClause', [$this, 'hookSelectWhere']); |
880f81a1 | 118 | $result = $this->callAPISuccess('report_template', 'getrows', [ |
119 | 'report_id' => $reportID, | |
120 | 'options' => ['metadata' => ['sql']], | |
121 | ]); | |
122 | $found = FALSE; | |
123 | foreach ($result['metadata']['sql'] as $sql) { | |
124 | if (strstr($sql, " = 'Organization' ")) { | |
125 | $found = TRUE; | |
126 | } | |
127 | } | |
128 | $this->assertTrue($found, $reportID); | |
129 | } | |
130 | ||
131 | /** | |
132 | * Get templates suitable for SelectWhere test. | |
133 | * | |
134 | * @return array | |
135 | */ | |
136 | public function getReportTemplatesSupportingSelectWhere() { | |
137 | $allTemplates = $this->getReportTemplates(); | |
138 | // Exclude all that do not work as of test being written. I have not dug into why not. | |
139 | $currentlyExcluded = [ | |
140 | 'contribute/repeat', | |
880f81a1 | 141 | 'member/summary', |
142 | 'event/summary', | |
143 | 'case/summary', | |
144 | 'case/timespent', | |
145 | 'case/demographics', | |
146 | 'contact/log', | |
147 | 'contribute/bookkeeping', | |
148 | 'grant/detail', | |
149 | 'event/incomesummary', | |
150 | 'case/detail', | |
151 | 'Mailing/bounce', | |
152 | 'Mailing/summary', | |
153 | 'grant/statistics', | |
154 | 'logging/contact/detail', | |
155 | 'logging/contact/summary', | |
156 | ]; | |
157 | foreach ($allTemplates as $index => $template) { | |
158 | $reportID = $template[0]; | |
159 | if (in_array($reportID, $currentlyExcluded) || stristr($reportID, 'has existing issues')) { | |
160 | unset($allTemplates[$index]); | |
161 | } | |
162 | } | |
163 | return $allTemplates; | |
164 | } | |
165 | ||
166 | /** | |
167 | * @param \CRM_Core_DAO $entity | |
168 | * @param array $clauses | |
169 | */ | |
170 | public function hookSelectWhere($entity, &$clauses) { | |
171 | // Restrict access to cases by type | |
172 | if ($entity == 'Contact') { | |
173 | $clauses['contact_type'][] = " = 'Organization' "; | |
174 | } | |
175 | } | |
176 | ||
e2779f6e | 177 | /** |
fe482240 | 178 | * Test getrows on contact summary report. |
e2779f6e | 179 | */ |
00be9182 | 180 | public function testReportTemplateGetRowsContactSummary() { |
5c49fee0 | 181 | $description = "Retrieve rows from a report template (optionally providing the instance_id)."; |
9099cab3 | 182 | $result = $this->callAPISuccess('report_template', 'getrows', [ |
e2779f6e | 183 | 'report_id' => 'contact/summary', |
9099cab3 CW |
184 | 'options' => ['metadata' => ['labels', 'title']], |
185 | ], __FUNCTION__, __FILE__, $description, 'Getrows'); | |
781d91c8 | 186 | $this->assertEquals('Contact Name', $result['metadata']['labels']['civicrm_contact_sort_name']); |
e2779f6e E |
187 | |
188 | //the second part of this test has been commented out because it relied on the db being reset to | |
189 | // it's base state | |
190 | //wasn't able to get that to work consistently | |
191 | // however, when the db is in the base state the tests do pass | |
192 | // and because the test covers 'all' contacts we can't create our own & assume the others don't exist | |
193 | /* | |
194 | $this->assertEquals(2, $result['count']); | |
195 | $this->assertEquals('Default Organization', $result[0]['civicrm_contact_sort_name']); | |
196 | $this->assertEquals('Second Domain', $result[1]['civicrm_contact_sort_name']); | |
197 | $this->assertEquals('15 Main St', $result[1]['civicrm_address_street_address']); | |
e70a7fc0 | 198 | */ |
e2779f6e E |
199 | } |
200 | ||
b736d2b6 SP |
201 | /** |
202 | * Test getrows on Mailing Opened report. | |
203 | */ | |
204 | public function testReportTemplateGetRowsMailingUniqueOpened() { | |
205 | $description = "Retrieve rows from a mailing opened report template."; | |
1d291c60 | 206 | $this->loadXMLDataSet(dirname(__FILE__) . '/../../CRM/Mailing/BAO/queryDataset.xml'); |
ead1a15e MD |
207 | |
208 | // Check total rows without distinct | |
209 | global $_REQUEST; | |
210 | $_REQUEST['distinct'] = 0; | |
9099cab3 | 211 | $result = $this->callAPIAndDocument('report_template', 'getrows', [ |
b736d2b6 | 212 | 'report_id' => 'Mailing/opened', |
9099cab3 CW |
213 | 'options' => ['metadata' => ['labels', 'title']], |
214 | ], __FUNCTION__, __FILE__, $description, 'Getrows'); | |
ead1a15e | 215 | $this->assertEquals(14, $result['count']); |
b736d2b6 | 216 | |
ead1a15e MD |
217 | // Check total rows with distinct |
218 | $_REQUEST['distinct'] = 1; | |
9099cab3 | 219 | $result = $this->callAPIAndDocument('report_template', 'getrows', [ |
ead1a15e | 220 | 'report_id' => 'Mailing/opened', |
9099cab3 CW |
221 | 'options' => ['metadata' => ['labels', 'title']], |
222 | ], __FUNCTION__, __FILE__, $description, 'Getrows'); | |
ead1a15e MD |
223 | $this->assertEquals(5, $result['count']); |
224 | ||
225 | // Check total rows with distinct by passing NULL value to distinct parameter | |
226 | $_REQUEST['distinct'] = NULL; | |
9099cab3 | 227 | $result = $this->callAPIAndDocument('report_template', 'getrows', [ |
ead1a15e | 228 | 'report_id' => 'Mailing/opened', |
9099cab3 CW |
229 | 'options' => ['metadata' => ['labels', 'title']], |
230 | ], __FUNCTION__, __FILE__, $description, 'Getrows'); | |
ead1a15e | 231 | $this->assertEquals(5, $result['count']); |
b736d2b6 SP |
232 | } |
233 | ||
e2779f6e | 234 | /** |
63dc1f23 | 235 | * Test api to get rows from reports. |
fe482240 | 236 | * |
e2779f6e | 237 | * @dataProvider getReportTemplates |
fe482240 | 238 | * |
1e1fdcf6 | 239 | * @param $reportID |
fe482240 | 240 | * |
a6439b6a | 241 | * @throws \PHPUnit\Framework\IncompleteTestError |
e2779f6e | 242 | */ |
00be9182 | 243 | public function testReportTemplateGetRowsAllReports($reportID) { |
63dc1f23 | 244 | //$reportID = 'logging/contact/summary'; |
22e263ad | 245 | if (stristr($reportID, 'has existing issues')) { |
e2779f6e E |
246 | $this->markTestIncomplete($reportID); |
247 | } | |
63dc1f23 | 248 | if (substr($reportID, 0, '7') === 'logging') { |
249 | Civi::settings()->set('logging', 1); | |
250 | } | |
251 | ||
9099cab3 | 252 | $this->callAPISuccess('report_template', 'getrows', [ |
92915c55 | 253 | 'report_id' => $reportID, |
9099cab3 | 254 | ]); |
63dc1f23 | 255 | if (substr($reportID, 0, '7') === 'logging') { |
256 | Civi::settings()->set('logging', 0); | |
257 | } | |
e2779f6e E |
258 | } |
259 | ||
f2f65d33 | 260 | /** |
261 | * Test logging report when a custom data table has a table removed by hook. | |
262 | * | |
263 | * Here we are checking that no fatal is triggered. | |
264 | */ | |
265 | public function testLoggingReportWithHookRemovalOfCustomDataTable() { | |
266 | Civi::settings()->set('logging', 1); | |
267 | $group1 = $this->customGroupCreate(); | |
268 | $group2 = $this->customGroupCreate(['name' => 'second_one', 'title' => 'second one', 'table_name' => 'civicrm_value_second_one']); | |
9099cab3 CW |
269 | $this->customFieldCreate(['custom_group_id' => $group1['id'], 'label' => 'field one']); |
270 | $this->customFieldCreate(['custom_group_id' => $group2['id'], 'label' => 'field two']); | |
271 | $this->hookClass->setHook('civicrm_alterLogTables', [$this, 'alterLogTablesRemoveCustom']); | |
f2f65d33 | 272 | |
9099cab3 | 273 | $this->callAPISuccess('report_template', 'getrows', [ |
f2f65d33 | 274 | 'report_id' => 'logging/contact/summary', |
9099cab3 | 275 | ]); |
f2f65d33 | 276 | Civi::settings()->set('logging', 0); |
277 | $this->customGroupDelete($group1['id']); | |
278 | $this->customGroupDelete($group2['id']); | |
279 | } | |
280 | ||
281 | /** | |
282 | * Remove one log table from the logging spec. | |
283 | * | |
284 | * @param array $logTableSpec | |
285 | */ | |
286 | public function alterLogTablesRemoveCustom(&$logTableSpec) { | |
287 | unset($logTableSpec['civicrm_value_second_one']); | |
e2779f6e E |
288 | } |
289 | ||
6c8223f6 | 290 | /** |
291 | * Test api to get rows from reports with ACLs enabled. | |
292 | * | |
293 | * Checking for lack of fatal error at the moment. | |
294 | * | |
295 | * @dataProvider getReportTemplates | |
296 | * | |
297 | * @param $reportID | |
298 | * | |
a6439b6a | 299 | * @throws \PHPUnit\Framework\IncompleteTestError |
6c8223f6 | 300 | */ |
301 | public function testReportTemplateGetRowsAllReportsACL($reportID) { | |
302 | if (stristr($reportID, 'has existing issues')) { | |
303 | $this->markTestIncomplete($reportID); | |
304 | } | |
9099cab3 CW |
305 | $this->hookClass->setHook('civicrm_aclWhereClause', [$this, 'aclWhereHookNoResults']); |
306 | $this->callAPISuccess('report_template', 'getrows', [ | |
6c8223f6 | 307 | 'report_id' => $reportID, |
9099cab3 | 308 | ]); |
6c8223f6 | 309 | } |
310 | ||
e2779f6e | 311 | /** |
fe482240 EM |
312 | * Test get statistics. |
313 | * | |
e2779f6e | 314 | * @dataProvider getReportTemplates |
fe482240 | 315 | * |
1e1fdcf6 | 316 | * @param $reportID |
fe482240 | 317 | * |
a6439b6a | 318 | * @throws \PHPUnit\Framework\IncompleteTestError |
e2779f6e | 319 | */ |
00be9182 | 320 | public function testReportTemplateGetStatisticsAllReports($reportID) { |
22e263ad | 321 | if (stristr($reportID, 'has existing issues')) { |
e2779f6e E |
322 | $this->markTestIncomplete($reportID); |
323 | } | |
9099cab3 | 324 | if (in_array($reportID, ['contribute/softcredit', 'contribute/bookkeeping'])) { |
e2779f6e E |
325 | $this->markTestIncomplete($reportID . " has non enotices when calling statistics fn"); |
326 | } | |
5c49fee0 | 327 | $description = "Get Statistics from a report (note there isn't much data to get in the test DB)."; |
9099cab3 | 328 | $result = $this->callAPIAndDocument('report_template', 'getstatistics', [ |
e2779f6e | 329 | 'report_id' => $reportID, |
9099cab3 | 330 | ], __FUNCTION__, __FILE__, $description, 'Getstatistics', 'getstatistics'); |
e2779f6e E |
331 | } |
332 | ||
333 | /** | |
fe482240 EM |
334 | * Data provider function for getting all templates. |
335 | * | |
336 | * Note that the function needs to | |
e2779f6e E |
337 | * be static so cannot use $this->callAPISuccess |
338 | */ | |
339 | public static function getReportTemplates() { | |
9099cab3 | 340 | $reportsToSkip = [ |
92915c55 | 341 | 'event/income' => 'I do no understand why but error is Call to undefined method CRM_Report_Form_Event_Income::from() in CRM/Report/Form.php on line 2120', |
92915c55 | 342 | 'contribute/history' => 'Declaration of CRM_Report_Form_Contribute_History::buildRows() should be compatible with CRM_Report_Form::buildRows($sql, &$rows)', |
9099cab3 | 343 | ]; |
e2779f6e | 344 | |
9099cab3 | 345 | $reports = civicrm_api3('report_template', 'get', ['return' => 'value', 'options' => ['limit' => 500]]); |
e2779f6e | 346 | foreach ($reports['values'] as $report) { |
22e263ad | 347 | if (empty($reportsToSkip[$report['value']])) { |
9099cab3 | 348 | $reportTemplates[] = [$report['value']]; |
e2779f6e E |
349 | } |
350 | else { | |
9099cab3 | 351 | $reportTemplates[] = [$report['value'] . " has existing issues : " . $reportsToSkip[$report['value']]]; |
e2779f6e E |
352 | } |
353 | } | |
354 | ||
e2779f6e E |
355 | return $reportTemplates; |
356 | } | |
96025800 | 357 | |
2c6b4783 | 358 | /** |
359 | * Get contribution templates that work with basic filter tests. | |
360 | * | |
361 | * These templates require minimal data config. | |
362 | */ | |
363 | public static function getContributionReportTemplates() { | |
9099cab3 | 364 | return [['contribute/summary'], ['contribute/detail'], ['contribute/repeat'], ['topDonor' => 'contribute/topDonor']]; |
eae0f0d9 | 365 | } |
366 | ||
367 | /** | |
368 | * Get contribution templates that work with basic filter tests. | |
369 | * | |
370 | * These templates require minimal data config. | |
371 | */ | |
372 | public static function getMembershipReportTemplates() { | |
9099cab3 | 373 | return [['member/detail']]; |
eae0f0d9 | 374 | } |
375 | ||
c69ecb12 | 376 | /** |
377 | * Get the membership and contribution reports to test. | |
378 | * | |
379 | * @return array | |
380 | */ | |
eae0f0d9 | 381 | public static function getMembershipAndContributionReportTemplatesForGroupTests() { |
382 | $templates = array_merge(self::getContributionReportTemplates(), self::getMembershipReportTemplates()); | |
383 | foreach ($templates as $key => $value) { | |
384 | if (array_key_exists('topDonor', $value)) { | |
385 | // Report is not standard enough to test here. | |
386 | unset($templates[$key]); | |
387 | } | |
388 | ||
389 | } | |
390 | return $templates; | |
2c6b4783 | 391 | } |
392 | ||
c160fde8 | 393 | /** |
394 | * Test Lybunt report to check basic inclusion of a contact who gave in the year before the chosen year. | |
395 | */ | |
396 | public function testLybuntReportWithData() { | |
397 | $inInd = $this->individualCreate(); | |
398 | $outInd = $this->individualCreate(); | |
9099cab3 CW |
399 | $this->contributionCreate(['contact_id' => $inInd, 'receive_date' => '2014-03-01']); |
400 | $this->contributionCreate(['contact_id' => $outInd, 'receive_date' => '2015-03-01', 'trxn_id' => NULL, 'invoice_id' => NULL]); | |
401 | $rows = $this->callAPISuccess('report_template', 'getrows', [ | |
c160fde8 | 402 | 'report_id' => 'contribute/lybunt', |
403 | 'yid_value' => 2015, | |
404 | 'yid_op' => 'calendar', | |
9099cab3 CW |
405 | 'options' => ['metadata' => ['sql']], |
406 | ]); | |
c160fde8 | 407 | $this->assertEquals(1, $rows['count'], "Report failed - the sql used to generate the results was " . print_r($rows['metadata']['sql'], TRUE)); |
408 | } | |
409 | ||
33072bc7 | 410 | /** |
411 | * Test Lybunt report applies ACLs. | |
412 | */ | |
413 | public function testLybuntReportWithDataAndACLFilter() { | |
9099cab3 | 414 | CRM_Core_Config::singleton()->userPermissionClass->permissions = ['administer CiviCRM']; |
33072bc7 | 415 | $inInd = $this->individualCreate(); |
416 | $outInd = $this->individualCreate(); | |
9099cab3 CW |
417 | $this->contributionCreate(['contact_id' => $inInd, 'receive_date' => '2014-03-01']); |
418 | $this->contributionCreate(['contact_id' => $outInd, 'receive_date' => '2015-03-01', 'trxn_id' => NULL, 'invoice_id' => NULL]); | |
419 | $this->hookClass->setHook('civicrm_aclWhereClause', [$this, 'aclWhereHookNoResults']); | |
420 | $params = [ | |
33072bc7 | 421 | 'report_id' => 'contribute/lybunt', |
422 | 'yid_value' => 2015, | |
423 | 'yid_op' => 'calendar', | |
9099cab3 | 424 | 'options' => ['metadata' => ['sql']], |
33072bc7 | 425 | 'check_permissions' => 1, |
9099cab3 | 426 | ]; |
33072bc7 | 427 | |
428 | $rows = $this->callAPISuccess('report_template', 'getrows', $params); | |
429 | $this->assertEquals(0, $rows['count'], "Report failed - the sql used to generate the results was " . print_r($rows['metadata']['sql'], TRUE)); | |
430 | ||
431 | CRM_Utils_Hook::singleton()->reset(); | |
432 | } | |
433 | ||
c160fde8 | 434 | /** |
435 | * Test Lybunt report to check basic inclusion of a contact who gave in the year before the chosen year. | |
436 | */ | |
437 | public function testLybuntReportWithFYData() { | |
438 | $inInd = $this->individualCreate(); | |
439 | $outInd = $this->individualCreate(); | |
9099cab3 CW |
440 | $this->contributionCreate(['contact_id' => $inInd, 'receive_date' => '2014-10-01']); |
441 | $this->contributionCreate(['contact_id' => $outInd, 'receive_date' => '2015-03-01', 'trxn_id' => NULL, 'invoice_id' => NULL]); | |
442 | $this->callAPISuccess('Setting', 'create', ['fiscalYearStart' => ['M' => 7, 'd' => 1]]); | |
443 | $rows = $this->callAPISuccess('report_template', 'getrows', [ | |
c160fde8 | 444 | 'report_id' => 'contribute/lybunt', |
445 | 'yid_value' => 2015, | |
446 | 'yid_op' => 'fiscal', | |
9099cab3 CW |
447 | 'options' => ['metadata' => ['sql']], |
448 | 'order_bys' => [ | |
449 | [ | |
c160fde8 | 450 | 'column' => 'first_name', |
451 | 'order' => 'ASC', | |
9099cab3 CW |
452 | ], |
453 | ], | |
454 | ]); | |
c160fde8 | 455 | |
456 | $this->assertEquals(2, $rows['count'], "Report failed - the sql used to generate the results was " . print_r($rows['metadata']['sql'], TRUE)); | |
15d9e604 | 457 | |
03843223 | 458 | $expected = preg_replace('/\s+/', ' ', 'DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci AS |
15d9e604 | 459 | SELECT SQL_CALC_FOUND_ROWS contact_civireport.id as cid FROM civicrm_contact contact_civireport INNER JOIN civicrm_contribution contribution_civireport USE index (received_date) ON contribution_civireport.contact_id = contact_civireport.id |
460 | AND contribution_civireport.is_test = 0 | |
461 | AND contribution_civireport.receive_date BETWEEN \'20140701000000\' AND \'20150630235959\' | |
1679e19c | 462 | WHERE contact_civireport.id NOT IN ( |
463 | SELECT cont_exclude.contact_id | |
464 | FROM civicrm_contribution cont_exclude | |
465 | WHERE cont_exclude.receive_date BETWEEN \'2015-7-1\' AND \'20160630235959\') | |
466 | AND ( contribution_civireport.contribution_status_id IN (1) ) | |
467 | GROUP BY contact_civireport.id'); | |
468 | // Exclude whitespace in comparison as we don't care if it changes & this allows us to make the above readable. | |
469 | $whitespacelessSql = preg_replace('/\s+/', ' ', $rows['metadata']['sql'][0]); | |
470 | $this->assertContains($expected, $whitespacelessSql); | |
c160fde8 | 471 | } |
472 | ||
3fd9a92a | 473 | /** |
474 | * Test Lybunt report to check basic inclusion of a contact who gave in the year before the chosen year. | |
475 | */ | |
476 | public function testLybuntReportWithFYDataOrderByLastYearAmount() { | |
477 | $inInd = $this->individualCreate(); | |
478 | $outInd = $this->individualCreate(); | |
9099cab3 CW |
479 | $this->contributionCreate(['contact_id' => $inInd, 'receive_date' => '2014-10-01']); |
480 | $this->contributionCreate(['contact_id' => $outInd, 'receive_date' => '2015-03-01', 'trxn_id' => NULL, 'invoice_id' => NULL]); | |
481 | $this->callAPISuccess('Setting', 'create', ['fiscalYearStart' => ['M' => 7, 'd' => 1]]); | |
482 | $rows = $this->callAPISuccess('report_template', 'getrows', [ | |
3fd9a92a | 483 | 'report_id' => 'contribute/lybunt', |
484 | 'yid_value' => 2015, | |
485 | 'yid_op' => 'fiscal', | |
9099cab3 CW |
486 | 'options' => ['metadata' => ['sql']], |
487 | 'fields' => ['first_name'], | |
488 | 'order_bys' => [ | |
489 | [ | |
3fd9a92a | 490 | 'column' => 'last_year_total_amount', |
491 | 'order' => 'ASC', | |
9099cab3 CW |
492 | ], |
493 | ], | |
494 | ]); | |
3fd9a92a | 495 | |
496 | $this->assertEquals(2, $rows['count'], "Report failed - the sql used to generate the results was " . print_r($rows['metadata']['sql'], TRUE)); | |
497 | } | |
498 | ||
2c6b4783 | 499 | /** |
500 | * Test the group filter works on the contribution summary (with a smart group). | |
eae0f0d9 | 501 | * |
502 | * @dataProvider getMembershipAndContributionReportTemplatesForGroupTests | |
503 | * | |
504 | * @param string $template | |
505 | * Name of the template to test. | |
2c6b4783 | 506 | */ |
eae0f0d9 | 507 | public function testContributionSummaryWithSmartGroupFilter($template) { |
2c6b4783 | 508 | $groupID = $this->setUpPopulatedSmartGroup(); |
9099cab3 | 509 | $rows = $this->callAPISuccess('report_template', 'getrows', [ |
eae0f0d9 | 510 | 'report_id' => $template, |
2c6b4783 | 511 | 'gid_value' => $groupID, |
512 | 'gid_op' => 'in', | |
9099cab3 CW |
513 | 'options' => ['metadata' => ['sql']], |
514 | ]); | |
eae0f0d9 | 515 | $this->assertNumberOfContactsInResult(3, $rows, $template); |
516 | if ($template === 'contribute/summary') { | |
517 | $this->assertEquals(3, $rows['values'][0]['civicrm_contribution_total_amount_count']); | |
518 | } | |
2c6b4783 | 519 | } |
520 | ||
521 | /** | |
eae0f0d9 | 522 | * Test the group filter works on the contribution summary. |
523 | * | |
524 | * @dataProvider getMembershipAndContributionReportTemplatesForGroupTests | |
2c6b4783 | 525 | */ |
eae0f0d9 | 526 | public function testContributionSummaryWithNotINSmartGroupFilter($template) { |
2c6b4783 | 527 | $groupID = $this->setUpPopulatedSmartGroup(); |
9099cab3 | 528 | $rows = $this->callAPISuccess('report_template', 'getrows', [ |
2c6b4783 | 529 | 'report_id' => 'contribute/summary', |
530 | 'gid_value' => $groupID, | |
43c1fa19 | 531 | 'gid_op' => 'notin', |
9099cab3 CW |
532 | 'options' => ['metadata' => ['sql']], |
533 | ]); | |
2c6b4783 | 534 | $this->assertEquals(2, $rows['values'][0]['civicrm_contribution_total_amount_count']); |
e6bab5ea | 535 | } |
2c6b4783 | 536 | |
0f880d50 | 537 | /** |
538 | * Test no fatal on order by per https://lab.civicrm.org/dev/core/issues/739 | |
539 | */ | |
540 | public function testCaseDetailsCaseTypeHeader() { | |
541 | $this->callAPISuccess('report_template', 'getrows', [ | |
542 | 'report_id' => 'case/detail', | |
543 | 'fields' => ['subject' => 1, 'client_sort_name' => 1], | |
39b959db SL |
544 | 'order_bys' => [ |
545 | 1 => [ | |
0f880d50 | 546 | 'column' => 'case_type_title', |
547 | 'order' => 'ASC', | |
548 | 'section' => '1', | |
549 | ], | |
550 | ], | |
551 | ]); | |
552 | } | |
553 | ||
e6bab5ea | 554 | /** |
555 | * Test the group filter works on the contribution summary. | |
556 | */ | |
557 | public function testContributionDetailSoftCredits() { | |
558 | $contactID = $this->individualCreate(); | |
559 | $contactID2 = $this->individualCreate(); | |
560 | $this->contributionCreate(['contact_id' => $contactID, 'api.ContributionSoft.create' => ['amount' => 5, 'contact_id' => $contactID2]]); | |
561 | $template = 'contribute/detail'; | |
9099cab3 | 562 | $rows = $this->callAPISuccess('report_template', 'getrows', [ |
e6bab5ea | 563 | 'report_id' => $template, |
564 | 'contribution_or_soft_value' => 'contributions_only', | |
565 | 'fields' => ['soft_credits' => 1, 'contribution_or_soft' => 1, 'sort_name' => 1], | |
9099cab3 CW |
566 | 'options' => ['metadata' => ['sql']], |
567 | ]); | |
e6bab5ea | 568 | $this->assertEquals( |
569 | "<a href='/index.php?q=civicrm/contact/view&reset=1&cid=" . $contactID2 . "'>Anderson, Anthony</a> $ 5.00", | |
570 | $rows['values'][0]['civicrm_contribution_soft_credits'] | |
571 | ); | |
2c6b4783 | 572 | } |
573 | ||
d70ada18 | 574 | /** |
575 | * Test the amount column is populated on soft credit details. | |
576 | */ | |
577 | public function testContributionDetailSoftCreditsOnly() { | |
578 | $contactID = $this->individualCreate(); | |
579 | $contactID2 = $this->individualCreate(); | |
580 | $this->contributionCreate(['contact_id' => $contactID, 'api.ContributionSoft.create' => ['amount' => 5, 'contact_id' => $contactID2]]); | |
581 | $template = 'contribute/detail'; | |
9099cab3 | 582 | $rows = $this->callAPISuccess('report_template', 'getrows', [ |
d70ada18 | 583 | 'report_id' => $template, |
584 | 'contribution_or_soft_value' => 'soft_credits_only', | |
585 | 'fields' => [ | |
586 | 'sort_name' => '1', | |
587 | 'email' => '1', | |
588 | 'financial_type_id' => '1', | |
589 | 'receive_date' => '1', | |
590 | 'total_amount' => '1', | |
591 | ], | |
9099cab3 CW |
592 | 'options' => ['metadata' => ['sql', 'labels']], |
593 | ]); | |
d70ada18 | 594 | foreach (array_keys($rows['metadata']['labels']) as $header) { |
595 | $this->assertTrue(!empty($rows['values'][0][$header])); | |
596 | } | |
597 | } | |
598 | ||
2c6b4783 | 599 | /** |
eae0f0d9 | 600 | * Test the group filter works on the various reports. |
2c6b4783 | 601 | * |
eae0f0d9 | 602 | * @dataProvider getMembershipAndContributionReportTemplatesForGroupTests |
2c6b4783 | 603 | * |
604 | * @param string $template | |
605 | * Report template unique identifier. | |
606 | */ | |
eae0f0d9 | 607 | public function testReportsWithNonSmartGroupFilter($template) { |
2c6b4783 | 608 | $groupID = $this->setUpPopulatedGroup(); |
9099cab3 | 609 | $rows = $this->callAPISuccess('report_template', 'getrows', [ |
2c6b4783 | 610 | 'report_id' => $template, |
9099cab3 | 611 | 'gid_value' => [$groupID], |
2c6b4783 | 612 | 'gid_op' => 'in', |
9099cab3 CW |
613 | 'options' => ['metadata' => ['sql']], |
614 | ]); | |
2c6b4783 | 615 | $this->assertNumberOfContactsInResult(1, $rows, $template); |
616 | } | |
617 | ||
618 | /** | |
619 | * Assert the included results match the expected. | |
620 | * | |
621 | * There may or may not be a group by in play so the assertion varies a little. | |
622 | * | |
623 | * @param int $numberExpected | |
624 | * @param array $rows | |
625 | * Rows returned from the report. | |
626 | * @param string $template | |
627 | */ | |
628 | protected function assertNumberOfContactsInResult($numberExpected, $rows, $template) { | |
629 | if (isset($rows['values'][0]['civicrm_contribution_total_amount_count'])) { | |
630 | $this->assertEquals($numberExpected, $rows['values'][0]['civicrm_contribution_total_amount_count'], 'wrong row count in ' . $template); | |
631 | } | |
632 | else { | |
633 | $this->assertEquals($numberExpected, count($rows['values']), 'wrong row count in ' . $template); | |
634 | } | |
635 | } | |
636 | ||
637 | /** | |
638 | * Test the group filter works on the contribution summary when 2 groups are involved. | |
639 | */ | |
640 | public function testContributionSummaryWithTwoGroups() { | |
641 | $groupID = $this->setUpPopulatedGroup(); | |
642 | $groupID2 = $this->setUpPopulatedSmartGroup(); | |
9099cab3 | 643 | $rows = $this->callAPISuccess('report_template', 'getrows', [ |
2c6b4783 | 644 | 'report_id' => 'contribute/summary', |
9099cab3 | 645 | 'gid_value' => [$groupID, $groupID2], |
2c6b4783 | 646 | 'gid_op' => 'in', |
9099cab3 CW |
647 | 'options' => ['metadata' => ['sql']], |
648 | ]); | |
2c6b4783 | 649 | $this->assertEquals(4, $rows['values'][0]['civicrm_contribution_total_amount_count']); |
650 | } | |
651 | ||
27367f58 | 652 | /** |
653 | * CRM-20640: Test the group filter works on the contribution summary when a single contact in 2 groups. | |
654 | */ | |
655 | public function testContributionSummaryWithSingleContactsInTwoGroups() { | |
656 | list($groupID1, $individualID) = $this->setUpPopulatedGroup(TRUE); | |
657 | // create second group and add the individual to it. | |
9099cab3 CW |
658 | $groupID2 = $this->groupCreate(['name' => uniqid(), 'title' => uniqid()]); |
659 | $this->callAPISuccess('GroupContact', 'create', [ | |
27367f58 | 660 | 'group_id' => $groupID2, |
661 | 'contact_id' => $individualID, | |
662 | 'status' => 'Added', | |
9099cab3 | 663 | ]); |
27367f58 | 664 | |
9099cab3 | 665 | $rows = $this->callAPISuccess('report_template', 'getrows', [ |
27367f58 | 666 | 'report_id' => 'contribute/summary', |
9099cab3 | 667 | 'gid_value' => [$groupID1, $groupID2], |
27367f58 | 668 | 'gid_op' => 'in', |
9099cab3 CW |
669 | 'options' => ['metadata' => ['sql']], |
670 | ]); | |
27367f58 | 671 | $this->assertEquals(1, $rows['count']); |
672 | } | |
673 | ||
2c6b4783 | 674 | /** |
675 | * Test the group filter works on the contribution summary when 2 groups are involved. | |
676 | */ | |
677 | public function testContributionSummaryWithTwoGroupsWithIntersection() { | |
678 | $groups = $this->setUpIntersectingGroups(); | |
679 | ||
9099cab3 | 680 | $rows = $this->callAPISuccess('report_template', 'getrows', [ |
2c6b4783 | 681 | 'report_id' => 'contribute/summary', |
682 | 'gid_value' => $groups, | |
683 | 'gid_op' => 'in', | |
9099cab3 CW |
684 | 'options' => ['metadata' => ['sql']], |
685 | ]); | |
2c6b4783 | 686 | $this->assertEquals(7, $rows['values'][0]['civicrm_contribution_total_amount_count']); |
687 | } | |
688 | ||
689 | /** | |
690 | * Set up a smart group for testing. | |
691 | * | |
692 | * The smart group includes all Households by filter. In addition an individual | |
693 | * is created and hard-added and an individual is created that is not added. | |
694 | * | |
695 | * One household is hard-added as well as being in the filter. | |
696 | * | |
697 | * This gives us a range of scenarios for testing contacts are included only once | |
698 | * whenever they are hard-added or in the criteria. | |
699 | * | |
700 | * @return int | |
701 | */ | |
702 | public function setUpPopulatedSmartGroup() { | |
703 | $household1ID = $this->householdCreate(); | |
704 | $individual1ID = $this->individualCreate(); | |
705 | $householdID = $this->householdCreate(); | |
706 | $individualID = $this->individualCreate(); | |
707 | $individualIDRemoved = $this->individualCreate(); | |
9099cab3 CW |
708 | $groupID = $this->smartGroupCreate([], ['name' => uniqid(), 'title' => uniqid()]); |
709 | $this->callAPISuccess('GroupContact', 'create', [ | |
2c6b4783 | 710 | 'group_id' => $groupID, |
711 | 'contact_id' => $individualIDRemoved, | |
712 | 'status' => 'Removed', | |
9099cab3 CW |
713 | ]); |
714 | $this->callAPISuccess('GroupContact', 'create', [ | |
2c6b4783 | 715 | 'group_id' => $groupID, |
716 | 'contact_id' => $individualID, | |
717 | 'status' => 'Added', | |
9099cab3 CW |
718 | ]); |
719 | $this->callAPISuccess('GroupContact', 'create', [ | |
2c6b4783 | 720 | 'group_id' => $groupID, |
721 | 'contact_id' => $householdID, | |
722 | 'status' => 'Added', | |
9099cab3 CW |
723 | ]); |
724 | foreach ([$household1ID, $individual1ID, $householdID, $individualID, $individualIDRemoved] as $contactID) { | |
725 | $this->contributionCreate(['contact_id' => $contactID, 'invoice_id' => '', 'trxn_id' => '']); | |
726 | $this->contactMembershipCreate(['contact_id' => $contactID]); | |
2c6b4783 | 727 | } |
728 | ||
729 | // Refresh the cache for test purposes. It would be better to alter to alter the GroupContact add function to add contacts to the cache. | |
0626851e | 730 | CRM_Contact_BAO_GroupContactCache::clearGroupContactCache($groupID); |
2c6b4783 | 731 | return $groupID; |
732 | } | |
733 | ||
734 | /** | |
eae0f0d9 | 735 | * Set up a static group for testing. |
2c6b4783 | 736 | * |
eae0f0d9 | 737 | * An individual is created and hard-added and an individual is created that is not added. |
2c6b4783 | 738 | * |
739 | * This gives us a range of scenarios for testing contacts are included only once | |
740 | * whenever they are hard-added or in the criteria. | |
741 | * | |
27367f58 | 742 | * @param bool $returnAddedContact |
743 | * | |
2c6b4783 | 744 | * @return int |
745 | */ | |
27367f58 | 746 | public function setUpPopulatedGroup($returnAddedContact = FALSE) { |
2c6b4783 | 747 | $individual1ID = $this->individualCreate(); |
748 | $individualID = $this->individualCreate(); | |
749 | $individualIDRemoved = $this->individualCreate(); | |
9099cab3 CW |
750 | $groupID = $this->groupCreate(['name' => uniqid(), 'title' => uniqid()]); |
751 | $this->callAPISuccess('GroupContact', 'create', [ | |
2c6b4783 | 752 | 'group_id' => $groupID, |
753 | 'contact_id' => $individualIDRemoved, | |
754 | 'status' => 'Removed', | |
9099cab3 CW |
755 | ]); |
756 | $this->callAPISuccess('GroupContact', 'create', [ | |
2c6b4783 | 757 | 'group_id' => $groupID, |
758 | 'contact_id' => $individualID, | |
759 | 'status' => 'Added', | |
9099cab3 | 760 | ]); |
2c6b4783 | 761 | |
9099cab3 CW |
762 | foreach ([$individual1ID, $individualID, $individualIDRemoved] as $contactID) { |
763 | $this->contributionCreate(['contact_id' => $contactID, 'invoice_id' => '', 'trxn_id' => '']); | |
764 | $this->contactMembershipCreate(['contact_id' => $contactID]); | |
2c6b4783 | 765 | } |
766 | ||
767 | // Refresh the cache for test purposes. It would be better to alter to alter the GroupContact add function to add contacts to the cache. | |
0626851e | 768 | CRM_Contact_BAO_GroupContactCache::clearGroupContactCache($groupID); |
27367f58 | 769 | |
770 | if ($returnAddedContact) { | |
9099cab3 | 771 | return [$groupID, $individualID]; |
27367f58 | 772 | } |
773 | ||
2c6b4783 | 774 | return $groupID; |
775 | } | |
776 | ||
777 | /** | |
778 | * @return array | |
779 | */ | |
780 | public function setUpIntersectingGroups() { | |
781 | $groupID = $this->setUpPopulatedGroup(); | |
782 | $groupID2 = $this->setUpPopulatedSmartGroup(); | |
783 | $addedToBothIndividualID = $this->individualCreate(); | |
784 | $removedFromBothIndividualID = $this->individualCreate(); | |
785 | $addedToSmartGroupRemovedFromOtherIndividualID = $this->individualCreate(); | |
786 | $removedFromSmartGroupAddedToOtherIndividualID = $this->individualCreate(); | |
9099cab3 | 787 | $this->callAPISuccess('GroupContact', 'create', [ |
2c6b4783 | 788 | 'group_id' => $groupID, |
789 | 'contact_id' => $addedToBothIndividualID, | |
790 | 'status' => 'Added', | |
9099cab3 CW |
791 | ]); |
792 | $this->callAPISuccess('GroupContact', 'create', [ | |
2c6b4783 | 793 | 'group_id' => $groupID2, |
794 | 'contact_id' => $addedToBothIndividualID, | |
795 | 'status' => 'Added', | |
9099cab3 CW |
796 | ]); |
797 | $this->callAPISuccess('GroupContact', 'create', [ | |
2c6b4783 | 798 | 'group_id' => $groupID, |
799 | 'contact_id' => $removedFromBothIndividualID, | |
800 | 'status' => 'Removed', | |
9099cab3 CW |
801 | ]); |
802 | $this->callAPISuccess('GroupContact', 'create', [ | |
2c6b4783 | 803 | 'group_id' => $groupID2, |
804 | 'contact_id' => $removedFromBothIndividualID, | |
805 | 'status' => 'Removed', | |
9099cab3 CW |
806 | ]); |
807 | $this->callAPISuccess('GroupContact', 'create', [ | |
2c6b4783 | 808 | 'group_id' => $groupID2, |
809 | 'contact_id' => $addedToSmartGroupRemovedFromOtherIndividualID, | |
810 | 'status' => 'Added', | |
9099cab3 CW |
811 | ]); |
812 | $this->callAPISuccess('GroupContact', 'create', [ | |
2c6b4783 | 813 | 'group_id' => $groupID, |
814 | 'contact_id' => $addedToSmartGroupRemovedFromOtherIndividualID, | |
815 | 'status' => 'Removed', | |
9099cab3 CW |
816 | ]); |
817 | $this->callAPISuccess('GroupContact', 'create', [ | |
2c6b4783 | 818 | 'group_id' => $groupID, |
819 | 'contact_id' => $removedFromSmartGroupAddedToOtherIndividualID, | |
820 | 'status' => 'Added', | |
9099cab3 CW |
821 | ]); |
822 | $this->callAPISuccess('GroupContact', 'create', [ | |
2c6b4783 | 823 | 'group_id' => $groupID2, |
824 | 'contact_id' => $removedFromSmartGroupAddedToOtherIndividualID, | |
825 | 'status' => 'Removed', | |
9099cab3 | 826 | ]); |
2c6b4783 | 827 | |
9099cab3 | 828 | foreach ([ |
39b959db SL |
829 | $addedToBothIndividualID, |
830 | $removedFromBothIndividualID, | |
831 | $addedToSmartGroupRemovedFromOtherIndividualID, | |
832 | $removedFromSmartGroupAddedToOtherIndividualID, | |
9099cab3 CW |
833 | ] as $contactID) { |
834 | $this->contributionCreate([ | |
2c6b4783 | 835 | 'contact_id' => $contactID, |
836 | 'invoice_id' => '', | |
837 | 'trxn_id' => '', | |
9099cab3 | 838 | ]); |
2c6b4783 | 839 | } |
9099cab3 | 840 | return [$groupID, $groupID2]; |
2c6b4783 | 841 | } |
842 | ||
45f8590c PN |
843 | /** |
844 | * Test Deferred Revenue Report. | |
845 | */ | |
846 | public function testDeferredRevenueReport() { | |
847 | $indv1 = $this->individualCreate(); | |
848 | $indv2 = $this->individualCreate(); | |
9099cab3 CW |
849 | $params = [ |
850 | 'contribution_invoice_settings' => [ | |
45f8590c | 851 | 'deferred_revenue_enabled' => '1', |
9099cab3 CW |
852 | ], |
853 | ]; | |
45f8590c PN |
854 | $this->callAPISuccess('Setting', 'create', $params); |
855 | $this->contributionCreate( | |
9099cab3 | 856 | [ |
45f8590c PN |
857 | 'contact_id' => $indv1, |
858 | 'receive_date' => '2016-10-01', | |
859 | 'revenue_recognition_date' => date('Y-m-t', strtotime(date('ymd') . '+3 month')), | |
860 | 'financial_type_id' => 2, | |
9099cab3 | 861 | ] |
45f8590c PN |
862 | ); |
863 | $this->contributionCreate( | |
9099cab3 | 864 | [ |
45f8590c PN |
865 | 'contact_id' => $indv1, |
866 | 'revenue_recognition_date' => date('Y-m-t', strtotime(date('ymd') . '+22 month')), | |
867 | 'financial_type_id' => 4, | |
868 | 'trxn_id' => NULL, | |
869 | 'invoice_id' => NULL, | |
9099cab3 | 870 | ] |
45f8590c PN |
871 | ); |
872 | $this->contributionCreate( | |
9099cab3 | 873 | [ |
45f8590c PN |
874 | 'contact_id' => $indv2, |
875 | 'revenue_recognition_date' => date('Y-m-t', strtotime(date('ymd') . '+1 month')), | |
876 | 'financial_type_id' => 4, | |
877 | 'trxn_id' => NULL, | |
878 | 'invoice_id' => NULL, | |
9099cab3 | 879 | ] |
45f8590c PN |
880 | ); |
881 | $this->contributionCreate( | |
9099cab3 | 882 | [ |
45f8590c PN |
883 | 'contact_id' => $indv2, |
884 | 'receive_date' => '2016-03-01', | |
885 | 'revenue_recognition_date' => date('Y-m-t', strtotime(date('ymd') . '+4 month')), | |
886 | 'financial_type_id' => 2, | |
887 | 'trxn_id' => NULL, | |
888 | 'invoice_id' => NULL, | |
9099cab3 | 889 | ] |
45f8590c | 890 | ); |
9099cab3 | 891 | $rows = $this->callAPISuccess('report_template', 'getrows', [ |
45f8590c | 892 | 'report_id' => 'contribute/deferredrevenue', |
9099cab3 | 893 | ]); |
45f8590c | 894 | $this->assertEquals(2, $rows['count'], "Report failed to get row count"); |
9099cab3 | 895 | $count = [2, 1]; |
45f8590c PN |
896 | foreach ($rows['values'] as $row) { |
897 | $this->assertEquals(array_pop($count), count($row['rows']), "Report failed to get row count"); | |
898 | } | |
899 | } | |
d7a896b4 | 900 | |
901 | /** | |
902 | * Test the custom data order by works when not in select. | |
903 | * | |
904 | * @dataProvider getMembershipAndContributionReportTemplatesForGroupTests | |
905 | * | |
906 | * @param string $template | |
907 | * Report template unique identifier. | |
908 | * | |
909 | * @throws \CRM_Core_Exception | |
910 | */ | |
911 | public function testReportsCustomDataOrderBy($template) { | |
912 | $this->entity = 'Contact'; | |
913 | $this->createCustomGroupWithFieldOfType(); | |
914 | $this->callAPISuccess('report_template', 'getrows', [ | |
915 | 'report_id' => $template, | |
916 | 'contribution_or_soft_value' => 'contributions_only', | |
917 | 'order_bys' => [['column' => 'custom_' . $this->ids['CustomField']['text'], 'order' => 'ASC']], | |
918 | ]); | |
919 | } | |
45f8590c | 920 | |
5e3dec81 JP |
921 | /** |
922 | * Test the group filter works on the various reports. | |
923 | * | |
924 | * @dataProvider getMembershipAndContributionReportTemplatesForGroupTests | |
925 | * | |
926 | * @param string $template | |
927 | * Report template unique identifier. | |
928 | */ | |
929 | public function testReportsWithNoTInSmartGroupFilter($template) { | |
930 | $groupID = $this->setUpPopulatedGroup(); | |
9099cab3 | 931 | $rows = $this->callAPISuccess('report_template', 'getrows', [ |
5e3dec81 | 932 | 'report_id' => $template, |
9099cab3 | 933 | 'gid_value' => [$groupID], |
5e3dec81 | 934 | 'gid_op' => 'notin', |
9099cab3 CW |
935 | 'options' => ['metadata' => ['sql']], |
936 | ]); | |
5e3dec81 JP |
937 | $this->assertNumberOfContactsInResult(2, $rows, $template); |
938 | } | |
939 | ||
5a14305b | 940 | /** |
d5625bc9 | 941 | * Test activity details report - requiring all current fields to be output. |
5a14305b | 942 | */ |
d5625bc9 | 943 | public function testActivityDetails() { |
5a14305b | 944 | $this->createContactsWithActivities(); |
945 | $fields = [ | |
946 | 'contact_source' => '1', | |
947 | 'contact_assignee' => '1', | |
948 | 'contact_target' => '1', | |
949 | 'contact_source_email' => '1', | |
950 | 'contact_assignee_email' => '1', | |
951 | 'contact_target_email' => '1', | |
952 | 'contact_source_phone' => '1', | |
953 | 'contact_assignee_phone' => '1', | |
954 | 'contact_target_phone' => '1', | |
955 | 'activity_type_id' => '1', | |
956 | 'activity_subject' => '1', | |
957 | 'activity_date_time' => '1', | |
958 | 'status_id' => '1', | |
959 | 'duration' => '1', | |
960 | 'location' => '1', | |
961 | 'details' => '1', | |
962 | 'priority_id' => '1', | |
963 | 'result' => '1', | |
964 | 'engagement_level' => '1', | |
965 | 'address_name' => '1', | |
966 | 'street_address' => '1', | |
967 | 'supplemental_address_1' => '1', | |
968 | 'supplemental_address_2' => '1', | |
969 | 'supplemental_address_3' => '1', | |
970 | 'street_number' => '1', | |
971 | 'street_name' => '1', | |
972 | 'street_unit' => '1', | |
973 | 'city' => '1', | |
974 | 'postal_code' => '1', | |
975 | 'postal_code_suffix' => '1', | |
976 | 'country_id' => '1', | |
977 | 'state_province_id' => '1', | |
978 | 'county_id' => '1', | |
979 | ]; | |
980 | $params = [ | |
981 | 'fields' => $fields, | |
982 | 'current_user_op' => 'eq', | |
983 | 'current_user_value' => '0', | |
984 | 'include_case_activities_op' => 'eq', | |
985 | 'include_case_activities_value' => 0, | |
87e5be4b | 986 | 'order_bys' => [ |
987 | 1 => ['column' => 'activity_date_time', 'order' => 'ASC'], | |
988 | 2 => ['column' => 'activity_type_id', 'order' => 'ASC'], | |
989 | ], | |
5a14305b | 990 | ]; |
991 | ||
992 | $params['report_id'] = 'Activity'; | |
993 | ||
994 | $rows = $this->callAPISuccess('report_template', 'getrows', $params)['values']; | |
995 | $expected = [ | |
996 | 'civicrm_contact_contact_source' => 'Łąchowski-Roberts, Anthony', | |
997 | 'civicrm_contact_contact_assignee' => '<a title=\'View Contact Summary for this Contact\' href=\'/index.php?q=civicrm/contact/view&reset=1&cid=4\'>Łąchowski-Roberts, Anthony</a>', | |
998 | 'civicrm_contact_contact_target' => '<a title=\'View Contact Summary for this Contact\' href=\'/index.php?q=civicrm/contact/view&reset=1&cid=3\'>Brzęczysław, Anthony</a>; <a title=\'View Contact Summary for this Contact\' href=\'/index.php?q=civicrm/contact/view&reset=1&cid=4\'>Łąchowski-Roberts, Anthony</a>', | |
999 | 'civicrm_contact_contact_source_id' => $this->contactIDs[2], | |
1000 | 'civicrm_contact_contact_assignee_id' => $this->contactIDs[1], | |
1001 | 'civicrm_contact_contact_target_id' => $this->contactIDs[0] . ';' . $this->contactIDs[1], | |
1002 | 'civicrm_email_contact_source_email' => 'anthony_anderson@civicrm.org', | |
1003 | 'civicrm_email_contact_assignee_email' => 'anthony_anderson@civicrm.org', | |
87e5be4b | 1004 | 'civicrm_email_contact_target_email' => 'techo@spying.com;anthony_anderson@civicrm.org', |
5a14305b | 1005 | 'civicrm_phone_contact_source_phone' => NULL, |
1006 | 'civicrm_phone_contact_assignee_phone' => NULL, | |
1007 | 'civicrm_phone_contact_target_phone' => NULL, | |
1008 | 'civicrm_activity_id' => '1', | |
1009 | 'civicrm_activity_source_record_id' => NULL, | |
1010 | 'civicrm_activity_activity_type_id' => 'Meeting', | |
1011 | 'civicrm_activity_activity_subject' => 'Very secret meeting', | |
486d6c5c | 1012 | 'civicrm_activity_activity_date_time' => date('Y-m-d 23:59:58', strtotime('now')), |
5a14305b | 1013 | 'civicrm_activity_status_id' => 'Scheduled', |
1014 | 'civicrm_activity_duration' => '120', | |
1015 | 'civicrm_activity_location' => 'Pennsylvania', | |
1016 | 'civicrm_activity_details' => 'a test activity', | |
1017 | 'civicrm_activity_priority_id' => 'Normal', | |
1018 | 'civicrm_address_address_name' => NULL, | |
1019 | 'civicrm_address_street_address' => NULL, | |
1020 | 'civicrm_address_supplemental_address_1' => NULL, | |
1021 | 'civicrm_address_supplemental_address_2' => NULL, | |
1022 | 'civicrm_address_supplemental_address_3' => NULL, | |
1023 | 'civicrm_address_street_number' => NULL, | |
1024 | 'civicrm_address_street_name' => NULL, | |
1025 | 'civicrm_address_street_unit' => NULL, | |
1026 | 'civicrm_address_city' => NULL, | |
1027 | 'civicrm_address_postal_code' => NULL, | |
1028 | 'civicrm_address_postal_code_suffix' => NULL, | |
1029 | 'civicrm_address_country_id' => NULL, | |
1030 | 'civicrm_address_state_province_id' => NULL, | |
1031 | 'civicrm_address_county_id' => NULL, | |
1032 | 'civicrm_contact_contact_source_link' => '/index.php?q=civicrm/contact/view&reset=1&cid=' . $this->contactIDs[2], | |
1033 | 'civicrm_contact_contact_source_hover' => 'View Contact Summary for this Contact', | |
1034 | 'civicrm_activity_activity_type_id_hover' => 'View Activity Record', | |
5a14305b | 1035 | ]; |
1036 | $row = $rows[0]; | |
1037 | // This link is not relative - skip for now | |
1038 | unset($row['civicrm_activity_activity_type_id_link']); | |
ab1ce467 | 1039 | if ($row['civicrm_email_contact_target_email'] === 'anthony_anderson@civicrm.org;techo@spying.com') { |
87e5be4b | 1040 | // order is unpredictable |
1041 | $expected['civicrm_email_contact_target_email'] = 'anthony_anderson@civicrm.org;techo@spying.com'; | |
1042 | } | |
5a14305b | 1043 | |
1044 | $this->assertEquals($expected, $row); | |
1045 | } | |
1046 | ||
1047 | /** | |
1048 | * Set up some activity data..... use some chars that challenge our utf handling. | |
1049 | */ | |
1050 | public function createContactsWithActivities() { | |
87e5be4b | 1051 | $this->contactIDs[] = $this->individualCreate(['last_name' => 'Brzęczysław', 'email' => 'techo@spying.com']); |
5a14305b | 1052 | $this->contactIDs[] = $this->individualCreate(['last_name' => 'Łąchowski-Roberts']); |
1053 | $this->contactIDs[] = $this->individualCreate(['last_name' => 'Łąchowski-Roberts']); | |
1054 | ||
1055 | $this->callAPISuccess('Activity', 'create', [ | |
1056 | 'subject' => 'Very secret meeting', | |
486d6c5c | 1057 | 'activity_date_time' => date('Y-m-d 23:59:58', strtotime('now')), |
5a14305b | 1058 | 'duration' => 120, |
1059 | 'location' => 'Pennsylvania', | |
1060 | 'details' => 'a test activity', | |
1061 | 'status_id' => 1, | |
1062 | 'activity_type_id' => 'Meeting', | |
1063 | 'source_contact_id' => $this->contactIDs[2], | |
9099cab3 | 1064 | 'target_contact_id' => [$this->contactIDs[0], $this->contactIDs[1]], |
5a14305b | 1065 | 'assignee_contact_id' => $this->contactIDs[1], |
1066 | ]); | |
1067 | } | |
1068 | ||
17c0ca6f AS |
1069 | /** |
1070 | * Test the group filter works on the contribution summary. | |
1071 | */ | |
1072 | public function testContributionDetailTotalHeader() { | |
1073 | $contactID = $this->individualCreate(); | |
1074 | $contactID2 = $this->individualCreate(); | |
1075 | $this->contributionCreate(['contact_id' => $contactID, 'api.ContributionSoft.create' => ['amount' => 5, 'contact_id' => $contactID2]]); | |
1076 | $template = 'contribute/detail'; | |
9099cab3 | 1077 | $rows = $this->callAPISuccess('report_template', 'getrows', [ |
17c0ca6f AS |
1078 | 'report_id' => $template, |
1079 | 'contribution_or_soft_value' => 'contributions_only', | |
1080 | 'fields' => [ | |
1081 | 'sort_name' => '1', | |
1082 | 'age' => '1', | |
1083 | 'email' => '1', | |
1084 | 'phone' => '1', | |
1085 | 'financial_type_id' => '1', | |
1086 | 'receive_date' => '1', | |
1087 | 'total_amount' => '1', | |
39b959db | 1088 | ], |
17c0ca6f | 1089 | 'order_bys' => [['column' => 'sort_name', 'order' => 'ASC', 'section' => '1']], |
9099cab3 CW |
1090 | 'options' => ['metadata' => ['sql']], |
1091 | ]); | |
17c0ca6f AS |
1092 | } |
1093 | ||
34496b9c | 1094 | /** |
1095 | * Test contact subtype filter on grant report. | |
1096 | */ | |
1097 | public function testGrantReportSeparatedFilter() { | |
1098 | $contactID = $this->individualCreate(['contact_sub_type' => ['Student', 'Parent']]); | |
1099 | $contactID2 = $this->individualCreate(); | |
1100 | $this->callAPISuccess('Grant', 'create', ['contact_id' => $contactID, 'status_id' => 1, 'grant_type_id' => 1, 'amount_total' => 1]); | |
1101 | $this->callAPISuccess('Grant', 'create', ['contact_id' => $contactID2, 'status_id' => 1, 'grant_type_id' => 1, 'amount_total' => 1]); | |
1102 | $rows = $this->callAPISuccess('report_template', 'getrows', [ | |
1103 | 'report_id' => 'grant/detail', | |
1104 | 'contact_sub_type_op' => 'in', | |
1105 | 'contact_sub_type_value' => ['Student'], | |
1106 | ]); | |
1107 | $this->assertEquals(1, $rows['count']); | |
1108 | } | |
1109 | ||
2fdf00e9 EL |
1110 | /** |
1111 | * Test contact subtype filter on summary report. | |
1112 | * | |
1113 | * @throws \CRM_Core_Exception | |
1114 | */ | |
1115 | public function testContactSubtypeNotNull() { | |
1116 | $this->individualCreate(['contact_sub_type' => ['Student', 'Parent']]); | |
1117 | $this->individualCreate(); | |
1118 | ||
1119 | $rows = $this->callAPISuccess('report_template', 'getrows', [ | |
1120 | 'report_id' => 'contact/summary', | |
1121 | 'contact_sub_type_op' => 'nnll', | |
1122 | 'contact_sub_type_value' => [], | |
1123 | 'contact_type_op' => 'eq', | |
1124 | 'contact_type_value' => 'Individual', | |
1125 | ]); | |
1126 | $this->assertEquals(1, $rows['count']); | |
1127 | } | |
1128 | ||
1129 | /** | |
1130 | * Test contact subtype filter on summary report. | |
1131 | * | |
1132 | * @throws \CRM_Core_Exception | |
1133 | */ | |
1134 | public function testContactSubtypeNull() { | |
1135 | $this->individualCreate(['contact_sub_type' => ['Student', 'Parent']]); | |
1136 | $this->individualCreate(); | |
1137 | ||
1138 | $rows = $this->callAPISuccess('report_template', 'getrows', [ | |
1139 | 'report_id' => 'contact/summary', | |
1140 | 'contact_sub_type_op' => 'nll', | |
1141 | 'contact_sub_type_value' => [], | |
1142 | 'contact_type_op' => 'eq', | |
1143 | 'contact_type_value' => 'Individual', | |
1144 | ]); | |
1145 | $this->assertEquals(1, $rows['count']); | |
1146 | } | |
1147 | ||
f8dcf338 AS |
1148 | /** |
1149 | * Test contact subtype filter on summary report. | |
1150 | * | |
1151 | * @throws \CRM_Core_Exception | |
1152 | */ | |
1153 | public function testContactSubtypeIn() { | |
1154 | $this->individualCreate(['contact_sub_type' => ['Student', 'Parent']]); | |
1155 | $this->individualCreate(); | |
1156 | ||
1157 | $rows = $this->callAPISuccess('report_template', 'getrows', [ | |
1158 | 'report_id' => 'contact/summary', | |
1159 | 'contact_sub_type_op' => 'in', | |
1160 | 'contact_sub_type_value' => ['Student'], | |
1161 | 'contact_type_op' => 'in', | |
1162 | 'contact_type_value' => 'Individual', | |
1163 | ]); | |
1164 | $this->assertEquals(1, $rows['count']); | |
1165 | } | |
1166 | ||
1167 | /** | |
1168 | * Test contact subtype filter on summary report. | |
1169 | * | |
1170 | * @throws \CRM_Core_Exception | |
1171 | */ | |
1172 | public function testContactSubtypeNotIn() { | |
1173 | $this->individualCreate(['contact_sub_type' => ['Student', 'Parent']]); | |
1174 | $this->individualCreate(); | |
1175 | ||
1176 | $rows = $this->callAPISuccess('report_template', 'getrows', [ | |
1177 | 'report_id' => 'contact/summary', | |
1178 | 'contact_sub_type_op' => 'notin', | |
1179 | 'contact_sub_type_value' => ['Student'], | |
1180 | 'contact_type_op' => 'in', | |
1181 | 'contact_type_value' => 'Individual', | |
1182 | ]); | |
1183 | $this->assertEquals(1, $rows['count']); | |
1184 | } | |
1185 | ||
e40ce31e JM |
1186 | /** |
1187 | * Test PCP report to ensure total donors and total committed is accurate. | |
1188 | */ | |
1189 | public function testPcpReportTotals() { | |
1190 | $donor1ContactId = $this->individualCreate(); | |
1191 | $donor2ContactId = $this->individualCreate(); | |
1192 | $donor3ContactId = $this->individualCreate(); | |
1193 | ||
1194 | // We are going to create two PCP pages. We will create two contributions | |
1195 | // on the first PCP page and one contribution on the second PCP page. | |
1196 | // | |
1197 | // Then, we will ensure that the first PCP page reports a total of both | |
1198 | // contributions (but not the contribution made on the second PCP page). | |
1199 | ||
1200 | // A PCP page requires three components: | |
1201 | // 1. A contribution page | |
1202 | // 2. A PCP Block | |
1203 | // 3. A PCP page | |
1204 | ||
1205 | // pcpBLockParams creates a contribution page and returns the parameters | |
1206 | // necessary to create a PBP Block. | |
1207 | $blockParams = $this->pcpBlockParams(); | |
1208 | $pcpBlock = CRM_PCP_BAO_PCPBlock::create($blockParams); | |
1209 | ||
1210 | // Keep track of the contribution page id created. We will use this | |
1211 | // contribution page id for all the PCP pages. | |
1212 | $contribution_page_id = $pcpBlock->entity_id; | |
1213 | ||
1214 | // pcpParams returns the parameters needed to create a PCP page. | |
1215 | $pcpParams = $this->pcpParams(); | |
1216 | // Keep track of the owner of the page so we can properly apply the | |
1217 | // soft credit. | |
1218 | $pcpOwnerContact1Id = $pcpParams['contact_id']; | |
1219 | $pcpParams['pcp_block_id'] = $pcpBlock->id; | |
1220 | $pcpParams['page_id'] = $contribution_page_id; | |
1221 | $pcpParams['page_type'] = 'contribute'; | |
1222 | $pcp1 = CRM_PCP_BAO_PCP::create($pcpParams); | |
1223 | ||
1224 | // Nice work. Now, let's create a second PCP page. | |
1225 | $pcpParams = $this->pcpParams(); | |
1226 | // Keep track of the owner of the page. | |
1227 | $pcpOwnerContact2Id = $pcpParams['contact_id']; | |
1228 | // We're using the same pcpBlock id and contribution page that we created above. | |
1229 | $pcpParams['pcp_block_id'] = $pcpBlock->id; | |
1230 | $pcpParams['page_id'] = $contribution_page_id; | |
1231 | $pcpParams['page_type'] = 'contribute'; | |
1232 | $pcp2 = CRM_PCP_BAO_PCP::create($pcpParams); | |
1233 | ||
1234 | // Get soft credit types, with the name column as the key. | |
9099cab3 | 1235 | $soft_credit_types = CRM_Contribute_BAO_ContributionSoft::buildOptions("soft_credit_type_id", NULL, ["flip" => TRUE, 'labelColumn' => 'name']); |
e40ce31e JM |
1236 | $pcp_soft_credit_type_id = $soft_credit_types['pcp']; |
1237 | ||
1238 | // Create two contributions assigned to this contribution page and | |
1239 | // assign soft credits appropriately. | |
1240 | // FIRST... | |
9099cab3 | 1241 | $contribution1params = [ |
e40ce31e JM |
1242 | 'contact_id' => $donor1ContactId, |
1243 | 'contribution_page_id' => $contribution_page_id, | |
1244 | 'total_amount' => '75.00', | |
9099cab3 | 1245 | ]; |
e40ce31e JM |
1246 | $c1 = $this->contributionCreate($contribution1params); |
1247 | // Now the soft contribution. | |
9099cab3 | 1248 | $p = [ |
e40ce31e JM |
1249 | 'contribution_id' => $c1, |
1250 | 'pcp_id' => $pcp1->id, | |
1251 | 'contact_id' => $pcpOwnerContact1Id, | |
1252 | 'amount' => 75.00, | |
1253 | 'currency' => 'USD', | |
1254 | 'soft_credit_type_id' => $pcp_soft_credit_type_id, | |
9099cab3 | 1255 | ]; |
e40ce31e JM |
1256 | $this->callAPISuccess('contribution_soft', 'create', $p); |
1257 | // SECOND... | |
9099cab3 | 1258 | $contribution2params = [ |
e40ce31e JM |
1259 | 'contact_id' => $donor2ContactId, |
1260 | 'contribution_page_id' => $contribution_page_id, | |
1261 | 'total_amount' => '25.00', | |
9099cab3 | 1262 | ]; |
e40ce31e JM |
1263 | $c2 = $this->contributionCreate($contribution2params); |
1264 | // Now the soft contribution. | |
9099cab3 | 1265 | $p = [ |
e40ce31e JM |
1266 | 'contribution_id' => $c2, |
1267 | 'pcp_id' => $pcp1->id, | |
1268 | 'contact_id' => $pcpOwnerContact1Id, | |
1269 | 'amount' => 25.00, | |
1270 | 'currency' => 'USD', | |
1271 | 'soft_credit_type_id' => $pcp_soft_credit_type_id, | |
9099cab3 | 1272 | ]; |
e40ce31e JM |
1273 | $this->callAPISuccess('contribution_soft', 'create', $p); |
1274 | ||
1275 | // Create one contributions assigned to the second PCP page | |
9099cab3 | 1276 | $contribution3params = [ |
e40ce31e JM |
1277 | 'contact_id' => $donor3ContactId, |
1278 | 'contribution_page_id' => $contribution_page_id, | |
1279 | 'total_amount' => '200.00', | |
9099cab3 | 1280 | ]; |
e40ce31e JM |
1281 | $c3 = $this->contributionCreate($contribution3params); |
1282 | // Now the soft contribution. | |
c69ecb12 | 1283 | $p = [ |
e40ce31e JM |
1284 | 'contribution_id' => $c3, |
1285 | 'pcp_id' => $pcp2->id, | |
1286 | 'contact_id' => $pcpOwnerContact2Id, | |
1287 | 'amount' => 200.00, | |
1288 | 'currency' => 'USD', | |
1289 | 'soft_credit_type_id' => $pcp_soft_credit_type_id, | |
c69ecb12 | 1290 | ]; |
e40ce31e JM |
1291 | $this->callAPISuccess('contribution_soft', 'create', $p); |
1292 | ||
1293 | $template = 'contribute/pcp'; | |
9099cab3 | 1294 | $rows = $this->callAPISuccess('report_template', 'getrows', [ |
e40ce31e JM |
1295 | 'report_id' => $template, |
1296 | 'title' => 'My PCP', | |
1297 | 'fields' => [ | |
1298 | 'amount_1' => '1', | |
1299 | 'soft_id' => '1', | |
39b959db | 1300 | ], |
9099cab3 | 1301 | ]); |
e40ce31e JM |
1302 | $values = $rows['values'][0]; |
1303 | $this->assertEquals(100.00, $values['civicrm_contribution_soft_amount_1_sum'], "Total commited should be $100"); | |
1304 | $this->assertEquals(2, $values['civicrm_contribution_soft_soft_id_count'], "Total donors should be 2"); | |
1305 | } | |
1306 | ||
38a85cdd J |
1307 | /** |
1308 | * Test a report that uses getAddressColumns(); | |
1309 | */ | |
1310 | public function testGetAddressColumns() { | |
1311 | $template = 'event/participantlisting'; | |
c69ecb12 | 1312 | $this->callAPISuccess('report_template', 'getrows', [ |
38a85cdd J |
1313 | 'report_id' => $template, |
1314 | 'fields' => [ | |
1315 | 'sort_name' => '1', | |
1316 | 'street_address' => '1', | |
1317 | ], | |
1318 | ]); | |
1319 | } | |
1320 | ||
6a488035 | 1321 | } |