From 17b6f179cb2ab479438dbcdff9b20ebc31b61d23 Mon Sep 17 00:00:00 2001 From: Eileen McNaughton Date: Fri, 6 Aug 2021 12:38:49 +1200 Subject: [PATCH] Reconcile contribution amount tokens This adds non_deductible_amount, as identified in https://github.com/civicrm/civicrm-core/pull/21034 It also moves the metadata functions to a parent class - this class might wind up being a temporary class as there are 3 other contenders for where this functionality should go - the TokenTrait, the abstractProvider or the tokenRow class. The intent is not to make that decision yet - just to provide visual clarity about what is generic Note - I've figured out how to switch over to v4 api for this but will do that in a separate PR --- CRM/Contribute/Tokens.php | 63 ++++----------- CRM/Core/EntityTokens.php | 78 +++++++++++++++++++ Civi/Token/TokenRow.php | 1 + .../Contribute/ActionMapping/ByTypeTest.php | 15 +++- 4 files changed, 106 insertions(+), 51 deletions(-) create mode 100644 CRM/Core/EntityTokens.php diff --git a/CRM/Contribute/Tokens.php b/CRM/Contribute/Tokens.php index e671979590fc..e37b637f5b9a 100644 --- a/CRM/Contribute/Tokens.php +++ b/CRM/Contribute/Tokens.php @@ -11,7 +11,6 @@ */ use Civi\ActionSchedule\Event\MailingQueryEvent; -use Civi\Token\AbstractTokenSubscriber; use Civi\Token\TokenProcessor; use Civi\Token\TokenRow; @@ -23,7 +22,7 @@ * At time of writing, we don't have any particularly special tokens -- we just * do some basic formatting based on the corresponding DB field. */ -class CRM_Contribute_Tokens extends AbstractTokenSubscriber { +class CRM_Contribute_Tokens extends CRM_Core_EntityTokens { /** * @return string @@ -59,6 +58,7 @@ protected function getPassthruTokens(): array { 'total_amount', 'fee_amount', 'net_amount', + 'non_deductible_amount', 'trxn_id', 'invoice_id', 'currency', @@ -155,49 +155,31 @@ public function alterActionScheduleQuery(MailingQueryEvent $e): void { /** * @inheritDoc + * @throws \CRM_Core_Exception */ public function evaluateToken(TokenRow $row, $entity, $field, $prefetch = NULL) { $actionSearchResult = $row->context['actionSearchResult']; $fieldValue = $actionSearchResult->{"contrib_$field"} ?? NULL; - if (in_array($field, ['total_amount', 'fee_amount', 'net_amount'])) { + if (array_key_exists($field, $this->getPseudoTokens())) { + $split = explode(':', $field); + return $row->tokens($entity, $field, $this->getPseudoValue($split[0], $split[1], $actionSearchResult->{"contrib_$split[0]"} ?? NULL)); + } + if ($this->isMoneyField($field)) { return $row->format('text/plain')->tokens($entity, $field, \CRM_Utils_Money::format($fieldValue, $actionSearchResult->contrib_currency)); } - elseif ($cfID = \CRM_Core_BAO_CustomField::getKeyID($field)) { - $row->customToken($entity, $cfID, $actionSearchResult->entity_id); - } - elseif (array_key_exists($field, $this->getPseudoTokens())) { - $split = explode(':', $field); - $row->tokens($entity, $field, $this->getPseudoValue($split[0], $split[1], $actionSearchResult->{"contrib_$split[0]"} ?? NULL)); - } - elseif (in_array($field, array_keys($this->getBasicTokens()))) { - $row->tokens($entity, $field, $fieldValue); + if ($this->isDateField($field)) { + return $row->format('text/plain')->tokens($entity, $field, \CRM_Utils_Date::customFormat($fieldValue)); } - elseif (!array_key_exists($field, CRM_Contribute_BAO_Contribution::fields())) { - if ($this->isDateField($field)) { - $row->format('text/plain')->tokens($entity, $field, \CRM_Utils_Date::customFormat($fieldValue)); - } - else { - $row->format('text/plain')->tokens($entity, $field, $fieldValue); - } + if ($cfID = \CRM_Core_BAO_CustomField::getKeyID($field)) { + $row->customToken($entity, $cfID, $actionSearchResult->entity_id); } else { - $row->dbToken($entity, $field, 'CRM_Contribute_BAO_Contribution', $field, $fieldValue); + $row->format('text/plain')->tokens($entity, $field, (string) $fieldValue); } } - /** - * Is the given field a date field. - * - * @param string $fieldName - * - * @return bool - */ - public function isDateField($fieldName): bool { - return $this->getFieldMetadata()[$fieldName]['type'] === (\CRM_Utils_Type::T_DATE + \CRM_Utils_Type::T_TIME); - } - /** * Get the value for the relevant pseudo field. * @@ -220,23 +202,4 @@ public function getPseudoValue(string $realField, string $pseudoKey, $fieldValue return (string) $fieldValue; } - /** - * Get the metadata for the available fields. - * - * @return array - */ - protected function getFieldMetadata(): array { - if (empty($this->fieldMetadata)) { - $baoName = $this->getBAOName(); - $fields = (array) $baoName::fields(); - // re-index by real field name. I originally wanted to use apiv4 - // getfields - but it returns different stuff for 'type' and - // does not return 'pseudoconstant' as a key so for now... - foreach ($fields as $details) { - $this->fieldMetadata[$details['name']] = $details; - } - } - return $this->fieldMetadata; - } - } diff --git a/CRM/Core/EntityTokens.php b/CRM/Core/EntityTokens.php new file mode 100644 index 000000000000..58ba7af0d5f9 --- /dev/null +++ b/CRM/Core/EntityTokens.php @@ -0,0 +1,78 @@ +getFieldMetadata()[$fieldName]['type'] === (\CRM_Utils_Type::T_DATE + \CRM_Utils_Type::T_TIME); + } + + /** + * Is the given field a date field. + * + * @param string $fieldName + * + * @return bool + */ + public function isMoneyField(string $fieldName): bool { + return $this->getFieldMetadata()[$fieldName]['type'] === (\CRM_Utils_Type::T_MONEY); + } + + /** + * Get the metadata for the available fields. + * + * @return array + */ + protected function getFieldMetadata(): array { + if (empty($this->fieldMetadata)) { + $baoName = $this->getBAOName(); + + $fields = (array) $baoName::fields(); + // re-index by real field name. I originally wanted to use apiv4 + // getfields - but it returns different stuff for 'type' and + // does not return 'pseudoconstant' as a key so for now... + foreach ($fields as $details) { + $this->fieldMetadata[$details['name']] = $details; + } + } + return $this->fieldMetadata; + } + +} diff --git a/Civi/Token/TokenRow.php b/Civi/Token/TokenRow.php index e150a32c4c4f..fb1fda5c2182 100644 --- a/Civi/Token/TokenRow.php +++ b/Civi/Token/TokenRow.php @@ -201,6 +201,7 @@ public function customToken($entity, $customFieldID, $entityID) { * @throws \CRM_Core_Exception */ public function dbToken($tokenEntity, $tokenField, $baoName, $baoField, $fieldValue) { + \CRM_Core_Error::deprecatedFunctionWarning('no alternative'); if ($fieldValue === NULL || $fieldValue === '') { return $this->tokens($tokenEntity, $tokenField, ''); } diff --git a/tests/phpunit/CRM/Contribute/ActionMapping/ByTypeTest.php b/tests/phpunit/CRM/Contribute/ActionMapping/ByTypeTest.php index ea5de5881e54..aa0dca703066 100644 --- a/tests/phpunit/CRM/Contribute/ActionMapping/ByTypeTest.php +++ b/tests/phpunit/CRM/Contribute/ActionMapping/ByTypeTest.php @@ -155,6 +155,7 @@ public function addAliceDues(): void { 'contact_id' => $this->contacts['alice']['id'], 'receive_date' => date('Ymd', strtotime($this->targetDate)), 'total_amount' => '100', + 'currency' => 'EUR', 'financial_type_id' => 1, 'non_deductible_amount' => '10', 'fee_amount' => '5', @@ -276,7 +277,11 @@ public function testTokenRendering(): void { financial type label = {contribution.financial_type_id:label} payment instrument id = {contribution.payment_instrument_id} payment instrument name = {contribution.payment_instrument_id:name} - payment instrument label = {contribution.payment_instrument_id:label}'; + payment instrument label = {contribution.payment_instrument_id:label} + non_deductible_amount = {contribution.non_deductible_amount} + total_amount = {contribution.total_amount} + net_amount = {contribution.net_amount} + fee_amount = {contribution.fee_amount}'; $this->schedule->save(); $this->callAPISuccess('job', 'send_reminder', []); @@ -296,6 +301,10 @@ public function testTokenRendering(): void { 'payment instrument id = 4', 'payment instrument name = Check', 'payment instrument label = Check', + 'non_deductible_amount = € 10.00', + 'total_amount = € 100.00', + 'net_amount = € 95.00', + 'fee_amount = € 5.00', ]; $this->mut->checkMailLog($expected); @@ -324,6 +333,10 @@ public function testTokenRendering(): void { 'payment instrument label = Check', 'legacy source SSF', 'source SSF', + 'non_deductible_amount = € 10.00', + 'total_amount = € 100.00', + 'net_amount = € 95.00', + 'fee_amount = € 5.00', ]; foreach ($expected as $string) { $this->assertStringContainsString($string, $contributionDetails[$this->contacts['alice']['id']]['html']);