From 0f4031daa5e48ea25ea56a896080287da7c20121 Mon Sep 17 00:00:00 2001 From: Eileen McNaughton Date: Tue, 31 Aug 2021 16:21:37 +1200 Subject: [PATCH] Add token processor for Contribution Recur This adds the token processor using our preferred outputs + 100% test cover. --- CRM/Contribute/RecurTokens.php | 36 ++++ CRM/Core/EntityTokens.php | 22 ++- Civi/Core/Container.php | 8 +- .../CRM/Utils/TokenConsistencyTest.php | 172 ++++++++++++++++++ 4 files changed, 227 insertions(+), 11 deletions(-) create mode 100644 CRM/Contribute/RecurTokens.php diff --git a/CRM/Contribute/RecurTokens.php b/CRM/Contribute/RecurTokens.php new file mode 100644 index 0000000000..3001eeb8b0 --- /dev/null +++ b/CRM/Contribute/RecurTokens.php @@ -0,0 +1,36 @@ +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())); } } diff --git a/Civi/Core/Container.php b/Civi/Core/Container.php index c680216844..96b85dbd4f 100644 --- a/Civi/Core/Container.php +++ b/Civi/Core/Container.php @@ -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) { diff --git a/tests/phpunit/CRM/Utils/TokenConsistencyTest.php b/tests/phpunit/CRM/Utils/TokenConsistencyTest.php index 4da85a0fb5..f0826af266 100644 --- a/tests/phpunit/CRM/Utils/TokenConsistencyTest.php +++ b/tests/phpunit/CRM/Utils/TokenConsistencyTest.php @@ -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'; + } + } -- 2.25.1