diff --git a/CRM/Report/Form.php b/CRM/Report/Form.php index 1dfb9becd23c..a5c1741f891b 100644 --- a/CRM/Report/Form.php +++ b/CRM/Report/Form.php @@ -2865,7 +2865,16 @@ public function groupBy() { $this->storeGroupByArray(); if (!empty($this->_groupByArray)) { - $this->_groupBy = CRM_Contact_BAO_Query::getGroupByFromSelectColumns($this->_selectClauses, $this->_groupByArray); + if ($this->optimisedForOnlyFullGroupBy) { + // We should probably deprecate this code path. What happens here is that + // the group by is amended to reflect the select columns. This often breaks the + // results. Retrofitting group strict group by onto existing report classes + // went badly. + $this->_groupBy = CRM_Contact_BAO_Query::getGroupByFromSelectColumns($this->_selectClauses, $this->_groupByArray); + } + else { + $this->_groupBy = ' GROUP BY ' . implode($this->_groupByArray); + } } } diff --git a/CRM/Report/Form/Contribute/PCP.php b/CRM/Report/Form/Contribute/PCP.php index f28d393c2ab8..7ae29f131fe3 100644 --- a/CRM/Report/Form/Contribute/PCP.php +++ b/CRM/Report/Form/Contribute/PCP.php @@ -125,6 +125,14 @@ public function __construct() { 'type' => CRM_Utils_Type::T_STRING, ), ), + 'group_bys' => array( + 'pcp_id' => array( + 'name' => 'id', + 'required' => TRUE, + 'default' => TRUE, + 'title' => ts('Personal Campaign Page'), + ), + ), 'grouping' => 'pcp-fields', ), 'civicrm_contribution_soft' => array( @@ -204,6 +212,7 @@ public function __construct() { ); parent::__construct(); + $this->optimisedForOnlyFullGroupBy = FALSE; } public function from() { @@ -236,10 +245,6 @@ public function from() { $this->addFinancialTrxnFromClause(); } - public function groupBy() { - $this->_groupBy = CRM_Contact_BAO_Query::getGroupByFromSelectColumns($this->_selectClauses, "{$this->_aliases['civicrm_pcp']}.id"); - } - public function orderBy() { $this->_orderBy = " ORDER BY {$this->_aliases['civicrm_contact']}.sort_name "; } diff --git a/tests/phpunit/CRM/PCP/BAO/PCPTest.php b/tests/phpunit/CRM/PCP/BAO/PCPTest.php index 2a90941bd827..d0c218914298 100644 --- a/tests/phpunit/CRM/PCP/BAO/PCPTest.php +++ b/tests/phpunit/CRM/PCP/BAO/PCPTest.php @@ -33,6 +33,8 @@ */ class CRM_PCP_BAO_PCPTest extends CiviUnitTestCase { + use CRMTraits_PCP_PCPTestTrait; + /** * Sets up the fixture, for example, opens a network connection. * This method is called before a test is executed. @@ -126,56 +128,6 @@ public function testDeletePCP() { ); } - /** - * Build params. - */ - private function pcpBlockParams() { - $contribPage = CRM_Core_DAO::createTestObject('CRM_Contribute_DAO_ContributionPage'); - $contribPageId = $contribPage->id; - $supporterProfile = CRM_Core_DAO::createTestObject('CRM_Core_DAO_UFGroup'); - $supporterProfileId = $supporterProfile->id; - - $params = array( - 'entity_table' => 'civicrm_contribution_page', - 'entity_id' => $contribPageId, - 'supporter_profile_id' => $supporterProfileId, - 'target_entity_id' => 1, - 'is_approval_needed' => 1, - 'is_tellfriend_enabled' => 1, - 'tellfriend_limit' => 1, - 'link_text' => 'Create your own PCP', - 'is_active' => 1, - ); - - return $params; - } - - /** - * Build params. - */ - private function pcpParams() { - $contact = CRM_Core_DAO::createTestObject('CRM_Contact_DAO_Contact'); - $contactId = $contact->id; - $contribPage = CRM_Core_DAO::createTestObject('CRM_Contribute_DAO_ContributionPage'); - $contribPageId = $contribPage->id; - - $params = array( - 'contact_id' => $contactId, - 'status_id' => '1', - 'title' => 'My PCP', - 'intro_text' => 'Hey you, contribute now!', - 'page_text' => 'You better give more.', - 'donate_link_text' => 'Donate Now', - 'page_id' => $contribPageId, - 'is_thermometer' => 1, - 'is_honor_roll' => 1, - 'goal_amount' => 10000.00, - 'is_active' => 1, - ); - - return $params; - } - /** * Tears down the fixture, for example, closes a network connection. * This method is called after a test is executed. diff --git a/tests/phpunit/CRMTraits/PCP/PCPTestTrait.php b/tests/phpunit/CRMTraits/PCP/PCPTestTrait.php new file mode 100644 index 000000000000..522a553d415a --- /dev/null +++ b/tests/phpunit/CRMTraits/PCP/PCPTestTrait.php @@ -0,0 +1,91 @@ +id; + $supporterProfile = CRM_Core_DAO::createTestObject('CRM_Core_DAO_UFGroup'); + $supporterProfileId = $supporterProfile->id; + + $params = array( + 'entity_table' => 'civicrm_contribution_page', + 'entity_id' => $contribPageId, + 'supporter_profile_id' => $supporterProfileId, + 'target_entity_id' => 1, + 'is_approval_needed' => 1, + 'is_tellfriend_enabled' => 1, + 'tellfriend_limit' => 1, + 'link_text' => 'Create your own PCP', + 'is_active' => 1, + ); + + return $params; + } + + /** + * Build and return pcp params. + * + * Create the necessary initial objects for a pcp page, then return the + * params needed to create the pcp page. + */ + public function pcpParams() { + $contact = CRM_Core_DAO::createTestObject('CRM_Contact_DAO_Contact'); + $contactId = $contact->id; + $contribPage = CRM_Core_DAO::createTestObject('CRM_Contribute_DAO_ContributionPage'); + $contribPageId = $contribPage->id; + + $params = array( + 'contact_id' => $contactId, + 'status_id' => '1', + 'title' => 'My PCP', + 'intro_text' => 'Hey you, contribute now!', + 'page_text' => 'You better give more.', + 'donate_link_text' => 'Donate Now', + 'page_id' => $contribPageId, + 'is_thermometer' => 1, + 'is_honor_roll' => 1, + 'goal_amount' => 10000.00, + 'is_active' => 1, + ); + + return $params; + } + +} diff --git a/tests/phpunit/api/v3/ReportTemplateTest.php b/tests/phpunit/api/v3/ReportTemplateTest.php index b19cc01a59e6..00b257d998c4 100644 --- a/tests/phpunit/api/v3/ReportTemplateTest.php +++ b/tests/phpunit/api/v3/ReportTemplateTest.php @@ -35,6 +35,7 @@ class api_v3_ReportTemplateTest extends CiviUnitTestCase { use CRMTraits_ACL_PermissionTrait; + use CRMTraits_PCP_PCPTestTrait; protected $_apiversion = 3; @@ -1071,4 +1072,125 @@ public function testContributionDetailTotalHeader() { )); } + /** + * Test PCP report to ensure total donors and total committed is accurate. + */ + public function testPcpReportTotals() { + $donor1ContactId = $this->individualCreate(); + $donor2ContactId = $this->individualCreate(); + $donor3ContactId = $this->individualCreate(); + + // We are going to create two PCP pages. We will create two contributions + // on the first PCP page and one contribution on the second PCP page. + // + // Then, we will ensure that the first PCP page reports a total of both + // contributions (but not the contribution made on the second PCP page). + + // A PCP page requires three components: + // 1. A contribution page + // 2. A PCP Block + // 3. A PCP page + + // pcpBLockParams creates a contribution page and returns the parameters + // necessary to create a PBP Block. + $blockParams = $this->pcpBlockParams(); + $pcpBlock = CRM_PCP_BAO_PCPBlock::create($blockParams); + + // Keep track of the contribution page id created. We will use this + // contribution page id for all the PCP pages. + $contribution_page_id = $pcpBlock->entity_id; + + // pcpParams returns the parameters needed to create a PCP page. + $pcpParams = $this->pcpParams(); + // Keep track of the owner of the page so we can properly apply the + // soft credit. + $pcpOwnerContact1Id = $pcpParams['contact_id']; + $pcpParams['pcp_block_id'] = $pcpBlock->id; + $pcpParams['page_id'] = $contribution_page_id; + $pcpParams['page_type'] = 'contribute'; + $pcp1 = CRM_PCP_BAO_PCP::create($pcpParams); + + // Nice work. Now, let's create a second PCP page. + $pcpParams = $this->pcpParams(); + // Keep track of the owner of the page. + $pcpOwnerContact2Id = $pcpParams['contact_id']; + // We're using the same pcpBlock id and contribution page that we created above. + $pcpParams['pcp_block_id'] = $pcpBlock->id; + $pcpParams['page_id'] = $contribution_page_id; + $pcpParams['page_type'] = 'contribute'; + $pcp2 = CRM_PCP_BAO_PCP::create($pcpParams); + + // Get soft credit types, with the name column as the key. + $soft_credit_types = CRM_Contribute_BAO_ContributionSoft::buildOptions("soft_credit_type_id", NULL, array("flip" => TRUE, 'labelColumn' => 'name')); + $pcp_soft_credit_type_id = $soft_credit_types['pcp']; + + // Create two contributions assigned to this contribution page and + // assign soft credits appropriately. + // FIRST... + $contribution1params = array( + 'contact_id' => $donor1ContactId, + 'contribution_page_id' => $contribution_page_id, + 'total_amount' => '75.00', + ); + $c1 = $this->contributionCreate($contribution1params); + // Now the soft contribution. + $p = array( + 'contribution_id' => $c1, + 'pcp_id' => $pcp1->id, + 'contact_id' => $pcpOwnerContact1Id, + 'amount' => 75.00, + 'currency' => 'USD', + 'soft_credit_type_id' => $pcp_soft_credit_type_id, + ); + $this->callAPISuccess('contribution_soft', 'create', $p); + // SECOND... + $contribution2params = array( + 'contact_id' => $donor2ContactId, + 'contribution_page_id' => $contribution_page_id, + 'total_amount' => '25.00', + ); + $c2 = $this->contributionCreate($contribution2params); + // Now the soft contribution. + $p = array( + 'contribution_id' => $c2, + 'pcp_id' => $pcp1->id, + 'contact_id' => $pcpOwnerContact1Id, + 'amount' => 25.00, + 'currency' => 'USD', + 'soft_credit_type_id' => $pcp_soft_credit_type_id, + ); + $this->callAPISuccess('contribution_soft', 'create', $p); + + // Create one contributions assigned to the second PCP page + $contribution3params = array( + 'contact_id' => $donor3ContactId, + 'contribution_page_id' => $contribution_page_id, + 'total_amount' => '200.00', + ); + $c3 = $this->contributionCreate($contribution3params); + // Now the soft contribution. + $p = array( + 'contribution_id' => $c3, + 'pcp_id' => $pcp2->id, + 'contact_id' => $pcpOwnerContact2Id, + 'amount' => 200.00, + 'currency' => 'USD', + 'soft_credit_type_id' => $pcp_soft_credit_type_id, + ); + $this->callAPISuccess('contribution_soft', 'create', $p); + + $template = 'contribute/pcp'; + $rows = $this->callAPISuccess('report_template', 'getrows', array( + 'report_id' => $template, + 'title' => 'My PCP', + 'fields' => [ + 'amount_1' => '1', + 'soft_id' => '1', + ], + )); + $values = $rows['values'][0]; + $this->assertEquals(100.00, $values['civicrm_contribution_soft_amount_1_sum'], "Total commited should be $100"); + $this->assertEquals(2, $values['civicrm_contribution_soft_soft_id_count'], "Total donors should be 2"); + } + }