dev/core#2450 - Update source/target contacts on contribution updates
authorManuel Flandorfer <mflandor@greenpeace.org>
Tue, 23 Mar 2021 11:01:21 +0000 (12:01 +0100)
committerManuel Flandorfer <mflandor@greenpeace.org>
Tue, 6 Apr 2021 08:52:15 +0000 (10:52 +0200)
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
tests/phpunit/CRM/Contribute/BAO/ContributionTest.php

index 5c1b3034ac0f89414067ac9a20fe263aaa6eeba2..7e030b8f38c23390a69a62972e007b378e541594 100644 (file)
@@ -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];
+  }
+
 }
index 65611892fcf74002abfc0aa730f8177d6ad9ea90..ce2e193a07493f52b5c70e796d867a328a56e28d 100644 (file)
@@ -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');
+  }
+
 }