Details
-
Type: Bug
-
Status: Done/Fixed
-
Priority: Trivial
-
Resolution: Fixed/Completed
-
Affects Version/s: 4.6
-
Fix Version/s: 4.7
-
Component/s: None
-
Labels:
-
Documentation Required?:None
-
Funding Source:Contributed Code
Description
The statistics fields on Repeating Contribution reports shows incorrect numbers.
_________________________________________________________________________
Here is the workflow as per the current code
(https://github.com/civicrm/civicrm-core/blob/master/CRM/Report/Form/Contribute/Repeat.php#L805)
1. Creates temp table having contribution of initial date range
CREATE TEMPORARY TABLE civicrm_temp_civireport_repeat1 (
contact_id int unsigned,
total_amount_sum int,
total_amount_count int
) ENGINE=HEAP DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
INSERT INTO civicrm_temp_civireport_repeat1
SELECT contribution1.contact_id,
sum( contribution1.total_amount ) AS total_amount_sum,
count( * ) AS total_amount_count
FROM civicrm_contribution contribution1 INNER JOIN civicrm_contribution_temp temp ON contribution1.id = temp.id
WHERE contribution1.is_test = 0 AND ( contribution1.receive_date >= 20140101 ) AND ( contribution1.receive_date <= 20141231 ) AND ( contribution1.contribution_status_id IN (1) )
GROUP BY contribution1.contact_id
2. Creates temp table having contribution of second date range
CREATE TEMPORARY TABLE civicrm_temp_civireport_repeat2 (
contact_id int unsigned,
total_amount_sum int,
total_amount_count int,
currency varchar(3)
) ENGINE=HEAP DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
INSERT INTO civicrm_temp_civireport_repeat2
SELECT contribution2.contact_id,
sum( contribution2.total_amount ) AS total_amount_sum,
count( * ) AS total_amount_count,
currency
FROM civicrm_contribution contribution2 INNER JOIN civicrm_contribution_temp temp ON contribution2.id = temp.id
WHERE contribution2.is_test = 0 AND ( contribution2.receive_date >= 20150101 ) AND ( contribution2.receive_date <= 20151231 ) AND ( contribution2.contribution_status_id IN (1) )
GROUP BY contribution2.contact_id
3. Merge's 2 tables from #1 and #2 joining with contact tables and add's Where clause
SELECT SQL_CALC_FOUND_ROWS contact_civireport.sort_name as contact_civireport_sort_name, contact_civireport.id as contact_civireport_id, contribution_civireport1.total_amount_count as contribution1_total_amount_count, contribution_civireport1.total_amount_sum as contribution1_total_amount_sum, contribution_civireport2.total_amount_count as contribution2_total_amount_count, contribution_civireport2.total_amount_sum as contribution2_total_amount_sum
FROM civicrm_contact contact_civireport
LEFT JOIN civicrm_address address_civireport ON contact_civireport.id = address_civireport.contact_id
LEFT JOIN civicrm_email email_civireport
ON contact_civireport.id = email_civireport.contact_id AND email_civireport.is_primary = 1
LEFT JOIN civicrm_phone phone_civireport
ON contact_civireport.id = phone_civireport.contact_id AND phone_civireport.is_primary = 1
LEFT JOIN civicrm_temp_civireport_repeat1 contribution_civireport1
ON contact_civireport.id = contribution_civireport1.contact_id
LEFT JOIN civicrm_temp_civireport_repeat2 contribution_civireport2
ON contact_civireport.id = contribution_civireport2.contact_id WHERE !(contribution_civireport1.total_amount_count IS NULL AND contribution_civireport2.total_amount_count IS NULL) AND contact_civireport.id IN (
SELECT DISTINCT cgroup_civireport.contact_id
FROM civicrm_group_contact cgroup_civireport
WHERE cgroup_civireport.group_id IN (5) AND cgroup_civireport.status = 'Added'
) GROUP BY contact_civireport.id LIMIT 0, 50
The sql at #1 and #2 for inserting records in temp table has all the records. The 3rd sql removes unwanted rows by adding filters in Where clause.
=========================================================================
For statistics(https://github.com/civicrm/civicrm-core/blob/master/CRM/Report/Form/Contribute/Repeat.php#L623)
1. Uses temp table's create at step #1 and #2. Unions it and store its in 3rd temp table
CREATE TEMPORARY TABLE civicrm_temp_civireport_repeat3
SELECT contact_id FROM civicrm_temp_civireport_repeat1 UNION SELECT contact_id FROM civicrm_temp_civireport_repeat2;
2. Below sql is used to calculate 'New Donor', 'lapsed donor's', 'upgraded donors', downgraded donors', 'maintained donors'. (https://github.com/civicrm/civicrm-core/blob/master/CRM/Report/Form/Contribute/Repeat.php#L660)
SELECT civicrm_temp_civireport_repeat3.contact_id,
civicrm_temp_civireport_repeat1.total_amount_sum as contribution1_total_amount_sum,
civicrm_temp_civireport_repeat2.total_amount_sum as contribution2_total_amount_sum
FROM civicrm_temp_civireport_repeat3
LEFT JOIN civicrm_temp_civireport_repeat1
ON civicrm_temp_civireport_repeat3.contact_id = civicrm_temp_civireport_repeat1.contact_id
LEFT JOIN civicrm_temp_civireport_repeat2
ON civicrm_temp_civireport_repeat3.contact_id = civicrm_temp_civireport_repeat2.contact_id
3. Below sql is used to get count of 'Initial Date Range', 'Total Amount', 'Total Donations', 'Average', 'Second Date Range', 'Total Amount', 'Total Donations', 'Average'.
SELECT COUNT(contribution_civireport1.total_amount_count ) as count,
SUM(contribution_civireport1.total_amount_sum ) as amount,
ROUND(AVG(contribution_civireport1.total_amount_sum), 2) as avg,
COUNT(contribution_civireport2.total_amount_count ) as count2,
SUM(contribution_civireport2.total_amount_sum ) as amount2,
ROUND(AVG(contribution_civireport2.total_amount_sum), 2) as avg2,
currency
FROM civicrm_contact contact_civireport
LEFT JOIN civicrm_address address_civireport ON contact_civireport.id = address_civireport.contact_id
LEFT JOIN civicrm_email email_civireport
ON contact_civireport.id = email_civireport.contact_id AND email_civireport.is_primary = 1
LEFT JOIN civicrm_phone phone_civireport
ON contact_civireport.id = phone_civireport.contact_id AND phone_civireport.is_primary = 1
LEFT JOIN civicrm_temp_civireport_repeat1 contribution_civireport1
ON contact_civireport.id = contribution_civireport1.contact_id
LEFT JOIN civicrm_temp_civireport_repeat2 contribution_civireport2
ON contact_civireport.id = contribution_civireport2.contact_id WHERE !(contribution_civireport1.total_amount_count IS NULL AND contribution_civireport2.total_amount_count IS NULL) AND contact_civireport.id IN (
SELECT DISTINCT cgroup_civireport.contact_id
FROM civicrm_group_contact cgroup_civireport
WHERE cgroup_civireport.group_id IN (5) AND cgroup_civireport.status = 'Added'
)
GROUP BY currency
When joining 2 tables it unions all the contribution without adding Where clause, which ends up calculating statistics for all contribution