From 6b5e660316bc6610aa1f0f4c642a7290b20aba7c Mon Sep 17 00:00:00 2001 From: Manuel Flandorfer Date: Tue, 23 Mar 2021 12:01:21 +0100 Subject: [PATCH] dev/core#2450 - Update source/target contacts on contribution updates dev/core#2450 - Incorporate requested changes dev/core#2450 - Make activity target contact parameter an array dev/core#2450 - Delete now obsolete comment about target contact updates dev/core#2450 - Compare activity contact IDs on contribution update to avoid unnecessary updates dev/core#2450 - Simplify API calls in unit test dev/core#2450 - Remove limit from activity contact query --- CRM/Contribute/BAO/Contribution.php | 52 +++++++++++- .../CRM/Contribute/BAO/ContributionTest.php | 79 +++++++++++++++++++ 2 files changed, 128 insertions(+), 3 deletions(-) diff --git a/CRM/Contribute/BAO/Contribution.php b/CRM/Contribute/BAO/Contribution.php index 5c1b3034ac..7e030b8f38 100644 --- a/CRM/Contribute/BAO/Contribution.php +++ b/CRM/Contribute/BAO/Contribution.php @@ -10,6 +10,7 @@ */ use Civi\Api4\Activity; +use Civi\Api4\ActivityContact; use Civi\Api4\Contribution; use Civi\Api4\ContributionRecur; use Civi\Api4\PaymentProcessor; @@ -533,12 +534,23 @@ class CRM_Contribute_BAO_Contribution extends CRM_Contribute_DAO_Contribution { 'id' => $existingActivity['id'] ?? NULL, ], $campaignParams); if (!$activityParams['id']) { - // Don't set target contacts on update as these will have been - // correctly created and we risk overwriting them with - // 'best guess' params. $activityParams['source_contact_id'] = (int) ($params['source_contact_id'] ?? (CRM_Core_Session::getLoggedInContactID() ?: $contribution->contact_id)); $activityParams['target_contact_id'] = ($activityParams['source_contact_id'] === (int) $contribution->contact_id) ? [] : [$contribution->contact_id]; } + else { + list($sourceContactId, $targetContactId) = self::getActivitySourceAndTarget($activityParams['id']); + + if (empty($targetContactId) && $sourceContactId != $contribution->contact_id) { + // If no target contact exists and the source contact is not equal to + // the contribution contact, update the source contact + $activityParams['source_contact_id'] = $contribution->contact_id; + } + elseif (isset($targetContactId) && $targetContactId != $contribution->contact_id) { + // If a target contact exists and it is not equal to the contribution + // contact, update the target contact + $activityParams['target_contact_id'] = [$contribution->contact_id]; + } + } Activity::save(FALSE)->addRecord($activityParams)->execute(); } @@ -5409,4 +5421,38 @@ LIMIT 1;"; return $values; } + /** + * Get the activity source and target contacts linked to a contribution + * + * @param $activityId + * + * @return array + */ + private static function getActivitySourceAndTarget($activityId): array { + $activityContactQuery = ActivityContact::get(FALSE)->setWhere([ + ['activity_id', '=', $activityId], + ['record_type_id:name', 'IN', ['Activity Source', 'Activity Targets']], + ])->execute(); + + $sourceContactKey = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_ActivityContact', 'record_type_id', 'Activity Source'); + $targetContactKey = CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_ActivityContact', 'record_type_id', 'Activity Targets'); + + $sourceContactId = NULL; + $targetContactId = NULL; + + for ($i = 0; $i < $activityContactQuery->count(); $i++) { + $record = $activityContactQuery->itemAt($i); + + if ($record['record_type_id'] === $sourceContactKey) { + $sourceContactId = $record['contact_id']; + } + + if ($record['record_type_id'] === $targetContactKey) { + $targetContactId = $record['contact_id']; + } + } + + return [$sourceContactId, $targetContactId]; + } + } diff --git a/tests/phpunit/CRM/Contribute/BAO/ContributionTest.php b/tests/phpunit/CRM/Contribute/BAO/ContributionTest.php index 65611892fc..ce2e193a07 100644 --- a/tests/phpunit/CRM/Contribute/BAO/ContributionTest.php +++ b/tests/phpunit/CRM/Contribute/BAO/ContributionTest.php @@ -1820,4 +1820,83 @@ WHERE eft.entity_id = %1 AND ft.to_financial_account_id <> %2"; $this->assertEquals(0, $count); } + /** + * Test activity contact is updated when contribution contact is changed + */ + public function testUpdateActivityContactOnContributionContactChange(): void { + $contactId_1 = $this->individualCreate(); + $contactId_2 = $this->individualCreate(); + $contactId_3 = $this->individualCreate(); + + $contributionParams = [ + 'financial_type_id' => 'Donation', + 'receive_date' => date('Y-m-d H:i:s'), + 'sequential' => TRUE, + 'total_amount' => 50, + ]; + + // Case 1: Only source contact, no target contact + + $contribution = $this->callAPISuccess('Contribution', 'create', array_merge( + $contributionParams, + ['contact_id' => $contactId_1] + ))['values'][0]; + + $activity = $this->callAPISuccessGetSingle('Activity', ['source_record_id' => $contribution['id']]); + + $activityContactParams = [ + 'activity_id' => $activity['id'], + 'record_type_id' => 'Activity Source', + ]; + + $activityContact = $this->callAPISuccessGetSingle('ActivityContact', $activityContactParams); + + $this->assertEquals($activityContact['contact_id'], $contactId_1, 'Check source contact ID matches the first contact'); + + $contribution = $this->callAPISuccess('Contribution', 'create', array_merge( + $contributionParams, + [ + 'id' => $contribution['id'], + 'contact_id' => $contactId_2, + ] + ))['values'][0]; + + $activityContact = $this->callAPISuccessGetSingle('ActivityContact', $activityContactParams); + + $this->assertEquals($activityContact['contact_id'], $contactId_2, 'Check source contact ID matches the second contact'); + + // Case 2: Source and target contact + + $contribution = $this->callAPISuccess('Contribution', 'create', array_merge( + $contributionParams, + [ + 'contact_id' => $contactId_1, + 'source_contact_id' => $contactId_3, + ] + ))['values'][0]; + + $activity = $this->callAPISuccessGetSingle('Activity', ['source_record_id' => $contribution['id']]); + + $activityContactParams = [ + 'activity_id' => $activity['id'], + 'record_type_id' => 'Activity Targets', + ]; + + $activityContact = $this->callAPISuccessGetSingle('ActivityContact', $activityContactParams); + + $this->assertEquals($activityContact['contact_id'], $contactId_1, 'Check target contact ID matches first contact'); + + $contribution = $this->callAPISuccess('Contribution', 'create', array_merge( + $contributionParams, + [ + 'id' => $contribution['id'], + 'contact_id' => $contactId_2, + ] + ))['values'][0]; + + $activityContact = $this->callAPISuccessGetSingle('ActivityContact', $activityContactParams); + + $this->assertEquals($activityContact['contact_id'], $contactId_2, 'Check target contact ID matches the second contact'); + } + } -- 2.25.1