Add token processor for Contribution Recur
authorEileen McNaughton <emcnaughton@wikimedia.org>
Tue, 31 Aug 2021 04:21:37 +0000 (16:21 +1200)
committerEileen McNaughton <emcnaughton@wikimedia.org>
Fri, 10 Sep 2021 02:59:48 +0000 (14:59 +1200)
This adds the token processor using our preferred outputs + 100% test cover.

CRM/Contribute/RecurTokens.php [new file with mode: 0644]
CRM/Core/EntityTokens.php
Civi/Core/Container.php
tests/phpunit/CRM/Utils/TokenConsistencyTest.php

diff --git a/CRM/Contribute/RecurTokens.php b/CRM/Contribute/RecurTokens.php
new file mode 100644 (file)
index 0000000..3001eeb
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ * Class CRM_Contribute_RecurTokens
+ *
+ * Generate "contribution_recur.*" tokens.
+ */
+class CRM_Contribute_RecurTokens extends CRM_Core_EntityTokens {
+
+  /**
+   * Get the entity name for api v4 calls.
+   *
+   * @return string
+   */
+  protected function getApiEntityName(): string {
+    return 'ContributionRecur';
+  }
+
+  /**
+   * @return array
+   */
+  public function getCurrencyFieldName(): array {
+    return ['currency'];
+  }
+
+}
index 9a9497ebfaa83d339c62bc1f3d2a7b5b9007830b..c3882b4bbf5959e87730d848d62cc84c1c2aa351 100644 (file)
@@ -41,9 +41,18 @@ class CRM_Core_EntityTokens extends AbstractTokenSubscriber {
     $fieldValue = $this->getFieldValue($row, $field);
 
     if ($this->isPseudoField($field)) {
+      if (!empty($fieldValue)) {
+        // If it's set here it has already been loaded in pre-fetch.
+        return $row->format('text/plain')->tokens($entity, $field, (string) $fieldValue);
+      }
+      // Once prefetch is fully standardised we can remove this - as long
+      // as tests pass we should be fine as tests cover this.
       $split = explode(':', $field);
       return $row->tokens($entity, $field, $this->getPseudoValue($split[0], $split[1], $this->getFieldValue($row, $split[0])));
     }
+    if ($this->isCustomField($field)) {
+      return $row->customToken($entity, \CRM_Core_BAO_CustomField::getKeyID($field), $this->getFieldValue($row, 'id'));
+    }
     if ($this->isMoneyField($field)) {
       return $row->format('text/plain')->tokens($entity, $field,
         \CRM_Utils_Money::format($fieldValue, $this->getCurrency($row)));
@@ -51,12 +60,7 @@ class CRM_Core_EntityTokens extends AbstractTokenSubscriber {
     if ($this->isDateField($field)) {
       return $row->format('text/plain')->tokens($entity, $field, \CRM_Utils_Date::customFormat($fieldValue));
     }
-    if ($this->isCustomField($field)) {
-      $row->customToken($entity, \CRM_Core_BAO_CustomField::getKeyID($field), $this->getFieldValue($row, 'id'));
-    }
-    else {
-      $row->format('text/plain')->tokens($entity, $field, (string) $fieldValue);
-    }
+    $row->format('text/plain')->tokens($entity, $field, (string) $fieldValue);
   }
 
   /**
@@ -120,7 +124,7 @@ class CRM_Core_EntityTokens extends AbstractTokenSubscriber {
    * @return array|string[]
    */
   public function getAllTokens(): array {
-    return array_merge($this->getBasicTokens(), $this->getPseudoTokens(), CRM_Utils_Token::getCustomFieldTokens('Contribution'));
+    return array_merge($this->getBasicTokens(), $this->getPseudoTokens(), CRM_Utils_Token::getCustomFieldTokens($this->getApiEntityName()));
   }
 
   /**
@@ -131,7 +135,7 @@ class CRM_Core_EntityTokens extends AbstractTokenSubscriber {
    * @return bool
    */
   public function isDateField(string $fieldName): bool {
-    return $this->getFieldMetadata()[$fieldName]['data_type'] === 'Timestamp';
+    return in_array($this->getFieldMetadata()[$fieldName]['data_type'], ['Timestamp', 'Date'], TRUE);
   }
 
   /**
@@ -405,7 +409,7 @@ class CRM_Core_EntityTokens extends AbstractTokenSubscriber {
   }
 
   public function getPrefetchFields(\Civi\Token\Event\TokenValueEvent $e): array {
-    return array_intersect($this->getActiveTokens($e), $this->getCurrencyFieldName(), array_keys($this->getAllTokens()));
+    return array_intersect(array_merge($this->getActiveTokens($e), $this->getCurrencyFieldName()), array_keys($this->getAllTokens()));
   }
 
 }
index c680216844c590b5c8ea76c17a3a94a93bfb6760..96b85dbd4fbd505d8c1c7c40713bde7e28190b3b 100644 (file)
@@ -323,16 +323,20 @@ class Container {
       []
     ))->addTag('kernel.event_subscriber')->setPublic(TRUE);
     $container->setDefinition("crm_mailing_action_tokens", new Definition(
-      "CRM_Mailing_ActionTokens",
+      'CRM_Mailing_ActionTokens',
       []
     ))->addTag('kernel.event_subscriber')->setPublic(TRUE);
 
     foreach (['Activity', 'Contribute', 'Event', 'Mailing', 'Member'] as $comp) {
-      $container->setDefinition("crm_" . strtolower($comp) . "_tokens", new Definition(
+      $container->setDefinition('crm_' . strtolower($comp) . '_tokens', new Definition(
         "CRM_{$comp}_Tokens",
         []
       ))->addTag('kernel.event_subscriber')->setPublic(TRUE);
     }
+    $container->setDefinition('crm_contribution_recur_tokens', new Definition(
+      'CRM_Contribute_RecurTokens',
+      []
+    ))->addTag('kernel.event_subscriber')->setPublic(TRUE);
 
     $dispatcherDefn = $container->getDefinition('dispatcher');
     foreach (\CRM_Core_DAO_AllCoreTables::getBaoClasses() as $baoEntity => $baoClass) {
index 4da85a0fb51d42d1e0ec1e9d4fb4903aa5dd04f5..f0826af266f5ac1d8913d3af50d40abf6bf84a1c 100644 (file)
@@ -9,6 +9,8 @@
  +--------------------------------------------------------------------+
  */
 
+use Civi\Token\TokenProcessor;
+
 /**
  * CRM_Utils_TokenConsistencyTest
  *
@@ -29,6 +31,13 @@ class CRM_Utils_TokenConsistencyTest extends CiviUnitTestCase {
    */
   protected $case;
 
+  /**
+   * Recurring contribution.
+   *
+   * @var array
+   */
+  protected $contributionRecur;
+
   /**
    * Post test cleanup.
    *
@@ -159,4 +168,167 @@ No
     return $this->case['id'];
   }
 
+  /**
+   * Test that contribution recur tokens are consistently rendered.
+   */
+  public function testContributionRecurTokenConsistency(): void {
+    $this->createLoggedInUser();
+    $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), [
+      'controller' => __CLASS__,
+      'smarty' => FALSE,
+      'schema' => ['contribution_recurId'],
+    ]);
+    $this->assertEquals($this->getContributionRecurTokens(), $tokenProcessor->listTokens());
+    $tokenString = implode("\n", array_keys($this->getContributionRecurTokens()));
+
+    $tokenProcessor->addMessage('html', $tokenString, 'text/plain');
+    $tokenProcessor->addRow(['contribution_recurId' => $this->getContributionRecurID()]);
+    $tokenProcessor->evaluate();
+    $this->assertEquals($this->getExpectedContributionRecurTokenOutPut(), $tokenProcessor->getRow(0)->render('html'));
+  }
+
+  /**
+   * Get the contribution recur tokens keyed by the token.
+   *
+   * e.g {contribution_recur.id}
+   *
+   * @return array
+   */
+  protected function getContributionRecurTokens(): array {
+    $return = [];
+    foreach ($this->getContributionRecurTokensByField() as $key => $value) {
+      $return['{contribution_recur.' . $key . '}'] = $value;
+    }
+    return $return;
+  }
+
+  protected function getContributionRecurTokensByField(): array {
+    return [
+      'id' => 'Recurring Contribution ID',
+      'amount' => 'Amount',
+      'currency' => 'Currency',
+      'frequency_unit' => 'Frequency Unit',
+      'frequency_interval' => 'Interval (number of units)',
+      'installments' => 'Number of Installments',
+      'start_date' => 'Start Date',
+      'create_date' => 'Created Date',
+      'modified_date' => 'Modified Date',
+      'cancel_date' => 'Cancel Date',
+      'cancel_reason' => 'Cancellation Reason',
+      'end_date' => 'Recurring Contribution End Date',
+      'processor_id' => 'Processor ID',
+      'payment_token_id' => 'Payment Token ID',
+      'trxn_id' => 'Transaction ID',
+      'invoice_id' => 'Invoice ID',
+      'contribution_status_id' => 'Status',
+      'is_test' => 'Test',
+      'cycle_day' => 'Cycle Day',
+      'next_sched_contribution_date' => 'Next Scheduled Contribution Date',
+      'failure_count' => 'Number of Failures',
+      'failure_retry_date' => 'Retry Failed Attempt Date',
+      'auto_renew' => 'Auto Renew',
+      'payment_processor_id' => 'Payment Processor ID',
+      'financial_type_id' => 'Financial Type ID',
+      'payment_instrument_id' => 'Payment Method',
+      'is_email_receipt' => 'Send email Receipt?',
+      'frequency_unit:label' => 'Frequency Unit',
+      'frequency_unit:name' => 'Machine name: Frequency Unit',
+      'contribution_status_id:label' => 'Status',
+      'contribution_status_id:name' => 'Machine name: Status',
+      'payment_processor_id:label' => 'Payment Processor',
+      'payment_processor_id:name' => 'Machine name: Payment Processor',
+      'financial_type_id:label' => 'Financial Type',
+      'financial_type_id:name' => 'Machine name: Financial Type',
+      'payment_instrument_id:label' => 'Payment Method',
+      'payment_instrument_id:name' => 'Machine name: Payment Method',
+    ];
+  }
+
+  /**
+   * Get contributionRecur ID.
+   *
+   * @return int
+   */
+  protected function getContributionRecurID(): int {
+    if (!isset($this->contributionRecur)) {
+      $paymentProcessorID = $this->processorCreate();
+      $this->contributionRecur = $this->callAPISuccess('ContributionRecur', 'create', [
+        'contact_id' => $this->getContactID(),
+        'status_id' => 1,
+        'is_email_receipt' => 1,
+        'start_date' => '2021-07-23 15:39:20',
+        'end_date' => '2021-07-26 18:07:20',
+        'cancel_date' => '2021-08-19 09:12:45',
+        'cancel_reason' => 'Because',
+        'amount' => 5990.99,
+        'currency' => 'EUR',
+        'frequency_unit' => 'year',
+        'frequency_interval' => 2,
+        'installments' => 24,
+        'payment_instrument_id' => 'Check',
+        'financial_type_id' => 'Member dues',
+        'processor_id' => 'abc',
+        'payment_processor_id' => $paymentProcessorID,
+        'trxn_id' => 123,
+        'invoice_id' => 'inv123',
+        'sequential' => 1,
+        'failure_retry_date' => '2020-01-03',
+        'auto_renew' => 1,
+        'cycle_day' => '15',
+        'is_test' => TRUE,
+        'payment_token_id' => $this->callAPISuccess('PaymentToken', 'create', [
+          'contact_id' => $this->getContactID(),
+          'token' => 456,
+          'payment_processor_id' => $paymentProcessorID,
+        ])['id'],
+      ])['values'][0];
+    }
+    return $this->contributionRecur['id'];
+  }
+
+  /**
+   * Get rendered output for contribution tokens.
+   *
+   * @return string
+   */
+  protected function getExpectedContributionRecurTokenOutPut(): string {
+    return $this->getContributionRecurID() . '
+€ 5,990.99
+EUR
+year
+2
+24
+July 23rd, 2021  3:39 PM
+' . CRM_Utils_Date::customFormat($this->contributionRecur['create_date']) . '
+' . CRM_Utils_Date::customFormat($this->contributionRecur['modified_date']) . '
+August 19th, 2021  9:12 AM
+Because
+July 26th, 2021  6:07 PM
+abc
+1
+123
+inv123
+2
+1
+15
+
+0
+January 3rd, 2020 12:00 AM
+1
+1
+2
+4
+1
+year
+year
+Pending Label**
+Pending
+Dummy
+Dummy
+Member Dues
+Member Dues
+Check
+Check';
+  }
+
 }