From ec090e0223d4c8471056d96df5054520dade4df2 Mon Sep 17 00:00:00 2001 From: Eileen McNaughton Date: Sat, 30 Sep 2023 17:55:10 +1300 Subject: [PATCH] Switch to a token for billingName & address This adds the token to the contribution entity & updates the event receipts to use it (including the ones we are gonna kill cos it was a simple replace). Note I updated the format function to access apiv4 style label fields & let the chips fall where they may re event & county (ie you probably wouldn't set county but if you did AND your address format is configured to show it who are we to say otherwise) --- CRM/Contribute/BAO/Contribution.php | 11 ++- CRM/Contribute/Tokens.php | 78 ++++++++++++------- .../Contribution/BasicContribution.php | 4 + CRM/Core/EntityTokens.php | 31 ++++++++ CRM/Event/Form/Participant.php | 7 +- CRM/Event/Tokens.php | 6 +- CRM/Event/WorkflowMessage/EventExamples.php | 4 + CRM/Utils/Address.php | 8 +- .../Contribute/ActionMapping/ByTypeTest.php | 3 + .../CRM/Event/Form/ParticipantTest.php | 8 +- .../CRM/Utils/TokenConsistencyTest.php | 29 ++++++- .../event_online_receipt_text.tpl | 4 +- .../event_offline_receipt_html.tpl | 6 +- .../event_offline_receipt_text.tpl | 6 +- .../event_online_receipt_html.tpl | 6 +- .../event_online_receipt_text.tpl | 6 +- 16 files changed, 155 insertions(+), 62 deletions(-) diff --git a/CRM/Contribute/BAO/Contribution.php b/CRM/Contribute/BAO/Contribution.php index d293c6bd0f..ffecfa2032 100644 --- a/CRM/Contribute/BAO/Contribution.php +++ b/CRM/Contribute/BAO/Contribution.php @@ -409,6 +409,8 @@ class CRM_Contribute_BAO_Contribution extends CRM_Contribute_DAO_Contribution im $billingFirstName = $params['billing_first_name'] ?? NULL; $billingMiddleName = $params['billing_middle_name'] ?? NULL; $billingLastName = $params['billing_last_name'] ?? NULL; + // Note this is NOT used when creating a billing address. It is probably passed + // the the payment processor - which is horrible & could maybe change. $addressParams['address_name'] = "{$billingFirstName}" . CRM_Core_DAO::VALUE_SEPARATOR . "{$billingMiddleName}" . CRM_Core_DAO::VALUE_SEPARATOR . "{$billingLastName}"; foreach ($billingFields as $value) { @@ -1664,8 +1666,13 @@ LEFT JOIN civicrm_option_value contribution_status ON (civicrm_contribution.cont $billingLocationTypeID = CRM_Core_BAO_LocationType::getBilling(); [$hasBillingField, $addressParams] = self::getBillingAddressParams($params, $billingLocationTypeID); if ($hasBillingField) { - $address = CRM_Core_BAO_Address::writeRecord($addressParams); - return $address->id; + $nameFields = [ + $params['billing_first_name'] ?? NULL, + $params['billing_middle_name'] ?? NULL, + $params['billing_last_name'] ?? NULL, + ]; + $addressParams['name'] = implode(' ', array_filter($nameFields)); + return (int) CRM_Core_BAO_Address::writeRecord($addressParams)->id; } return NULL; diff --git a/CRM/Contribute/Tokens.php b/CRM/Contribute/Tokens.php index 5af208998f..625496528f 100644 --- a/CRM/Contribute/Tokens.php +++ b/CRM/Contribute/Tokens.php @@ -10,8 +10,8 @@ +--------------------------------------------------------------------+ */ -use Civi\Api4\ContributionPage; -use Civi\Api4\ContributionRecur; +use Civi\Api4\Address; +use Civi\Token\TokenRow; /** * Class CRM_Contribute_Tokens @@ -61,35 +61,61 @@ class CRM_Contribute_Tokens extends CRM_Core_EntityTokens { if (!array_key_exists('Contribution', \Civi::service('action_object_provider')->getEntities())) { return $tokens; } + $tokens += $this->getRelatedTokensForEntity('Address', 'address_id', ['name', 'id']); + + $tokens['address_id.name']['title'] = ts('Billing Address Name'); + $tokens['address_id.display'] = [ + 'title' => ts('Billing Address'), + 'name' => 'address_id.display', + 'type' => 'mapped', + 'input_type' => 'Text', + 'audience' => 'user', + 'data_type' => 'String', + ]; + // Ideally we would derive this from 'usage' - but it looks like adding the usage data // was quite a bit of work & didn't leave the energy to implement - esp expose for // where clauses (also, it feels like 'hidden+token' would be a good usage. - $tokenList = ['frontend_title', 'pay_later_text', 'pay_later_receipt', 'is_share', 'receipt_text']; - $contributionPageTokens = ContributionPage::getFields(FALSE)->addWhere('name', 'IN', $tokenList)->execute(); - foreach ($contributionPageTokens as $contributionPageToken) { - $tokens['contribution_page_id.' . $contributionPageToken['name']] = [ - 'title' => $contributionPageToken['title'], - 'name' => 'contribution_page_id.' . $contributionPageToken['name'], - 'type' => 'mapped', - 'data_type' => $contributionPageToken['data_type'], - 'input_type' => $contributionPageToken['input_type'], - 'audience' => $contributionPageToken['name'] === 'is_share' ? 'hidden' : 'user', - ]; - } + $contributionPageTokens = ['frontend_title', 'pay_later_text', 'pay_later_receipt', 'is_share', 'receipt_text']; + $tokens += $this->getRelatedTokensForEntity('ContributionPage', 'contribution_page_id', $contributionPageTokens, ['is_share']); + $hiddenTokens = ['modified_date', 'create_date', 'trxn_id', 'invoice_id', 'is_test', 'payment_token_id', 'payment_processor_id', 'payment_instrument_id', 'cycle_day', 'installments', 'processor_id', 'next_sched_contribution_date', 'failure_count', 'failure_retry_date', 'auto_renew', 'is_email_receipt', 'contribution_status_id']; - $contributionRecurFields = ContributionRecur::getFields(FALSE)->setLoadOptions(TRUE)->execute(); - foreach ($contributionRecurFields as $contributionRecurField) { - $tokens['contribution_recur_id.' . $contributionRecurField['name']] = [ - 'title' => $contributionRecurField['title'], - 'name' => 'contribution_recur_id.' . $contributionRecurField['name'], - 'type' => 'mapped', - 'options' => $contributionRecurField['options'] ?? NULL, - 'data_type' => $contributionRecurField['data_type'], - 'input_type' => $contributionRecurField['input_type'], - 'audience' => in_array($contributionRecurField['name'], $hiddenTokens) ? 'hidden' : 'user', - ]; - } + $tokens += $this->getRelatedTokensForEntity('ContributionRecur', 'contribution_recur_id', ['*'], $hiddenTokens); return $tokens; } + /** + * @param \Civi\Token\TokenRow $row + * @param string $field + * @return string|int + */ + protected function getFieldValue(TokenRow $row, string $field) { + $entityName = $this->getEntityName(); + if (isset($row->context[$entityName][$field])) { + return $row->context[$entityName][$field]; + } + if ($field === 'address_id.display') { + $addressID = $this->getFieldValue($row, 'address_id.id'); + // We possibly could figure out how to load in a cleverer way + // or as part of apiv4 but this is tested so that can easily happen later... + $address = Address::get(FALSE) + ->addWhere('id', '=', $addressID) + ->addSelect('*', 'state_province_id:label', 'country_id:label') + ->execute()->first(); + // We have name in the address_id.name token. + unset($address['name']); + return \CRM_Utils_Address::format($address); + } + return parent::getFieldValue($row, $field); + } + + /** + * Get fields which need to be returned to render another token. + * + * @return array + */ + public function getDependencies(): array { + return ['address_id.display' => 'address_id.id']; + } + } diff --git a/CRM/Contribute/WorkflowMessage/Contribution/BasicContribution.php b/CRM/Contribute/WorkflowMessage/Contribution/BasicContribution.php index d3477bb3da..4c5bdc364f 100644 --- a/CRM/Contribute/WorkflowMessage/Contribution/BasicContribution.php +++ b/CRM/Contribute/WorkflowMessage/Contribution/BasicContribution.php @@ -143,6 +143,10 @@ class CRM_Contribute_WorkflowMessage_Contribution_BasicContribution extends Work $this->setLineItem($mockOrder, $priceField, $priceFieldValue, $index); } + $contribution['address_id.name'] = 'Barbara Johnson'; + $contribution['address_id.display'] = '790L Lincoln St S +Baltimore, New York 10545 +United States'; $contribution['total_amount'] = $mockOrder->getTotalAmount(); $contribution['tax_amount'] = $mockOrder->getTotalTaxAmount() ? round($mockOrder->getTotalTaxAmount(), 2) : 0; $messageTemplate->setContribution($contribution); diff --git a/CRM/Core/EntityTokens.php b/CRM/Core/EntityTokens.php index 850f5848de..ce76c40e23 100644 --- a/CRM/Core/EntityTokens.php +++ b/CRM/Core/EntityTokens.php @@ -701,4 +701,35 @@ class CRM_Core_EntityTokens extends AbstractTokenSubscriber { return $cacheKey; } + /** + * Get metadata for tokens for a related entity joined by a field on the main entity. + * + * @param string $entity + * @param string $joinField + * @param array $tokenList + * @param array $hiddenTokens + * + * @return array + * @throws \CRM_Core_Exception + */ + protected function getRelatedTokensForEntity(string $entity, string $joinField, array $tokenList, $hiddenTokens = []): array { + $apiParams = ['checkPermissions' => FALSE]; + if ($tokenList !== ['*']) { + $apiParams['where'] = [['name', 'IN', $tokenList]]; + } + $relatedTokens = civicrm_api4($entity, 'getFields', $apiParams); + $tokens = []; + foreach ($relatedTokens as $relatedToken) { + $tokens[$joinField . '.' . $relatedToken['name']] = [ + 'title' => $relatedToken['title'], + 'name' => $joinField . '.' . $relatedToken['name'], + 'type' => 'mapped', + 'data_type' => $relatedToken['data_type'], + 'input_type' => $relatedToken['input_type'], + 'audience' => in_array($relatedToken['name'], $hiddenTokens, TRUE) ? 'hidden' : 'user', + ]; + } + return $tokens; + } + } diff --git a/CRM/Event/Form/Participant.php b/CRM/Event/Form/Participant.php index 2d9f32e6e3..913e403913 100644 --- a/CRM/Event/Form/Participant.php +++ b/CRM/Event/Form/Participant.php @@ -1693,12 +1693,7 @@ class CRM_Event_Form_Participant extends CRM_Contribute_Form_AbstractEditPayment } $contribParams['revenue_recognition_date'] = $this->getRevenueRecognitionDate(); - //create an contribution address - // The concept of contributeMode is deprecated. Elsewhere we use the function processBillingAddress() - although - // currently that is only inherited by back-office forms. - if ($form->_contributeMode != 'notify' && empty($params['is_pay_later'])) { - $contribParams['address_id'] = CRM_Contribute_BAO_Contribution::createAddress($params, $form->_bltID); - } + $contribParams['address_id'] = CRM_Contribute_BAO_Contribution::createAddress($params, $form->_bltID); $contribParams['skipLineItem'] = 1; $contribParams['skipCleanMoney'] = 1; diff --git a/CRM/Event/Tokens.php b/CRM/Event/Tokens.php index 2a51923ba0..258ea73bfb 100644 --- a/CRM/Event/Tokens.php +++ b/CRM/Event/Tokens.php @@ -204,11 +204,7 @@ class CRM_Event_Tokens extends CRM_Core_EntityTokens { 'custom.*', ], $this->getExposedFields())) ->execute()->first(); - $addressValues = [ - 'address_name' => $event['loc_block_id.address_id.name'], - 'state_province' => $event['loc_block_id.address_id.state_province_id:label'], - 'country' => $event['loc_block_id.address_id.country_id:label'], - ]; + $addressValues = ['address_name' => $event['loc_block_id.address_id.name']]; foreach ($event as $key => $value) { if (strpos($key, 'loc_block_id.address_id.') === 0) { $addressValues[str_replace('loc_block_id.address_id.', '', $key)] = $value; diff --git a/CRM/Event/WorkflowMessage/EventExamples.php b/CRM/Event/WorkflowMessage/EventExamples.php index b5a505868c..e7b4ade77b 100644 --- a/CRM/Event/WorkflowMessage/EventExamples.php +++ b/CRM/Event/WorkflowMessage/EventExamples.php @@ -112,6 +112,10 @@ class CRM_Event_WorkflowMessage_EventExamples extends WorkflowMessageExample { $contribution['total_amount'] = $mockOrder->getTotalAmount(); $contribution['tax_amount'] = $mockOrder->getTotalTaxAmount() ? round($mockOrder->getTotalTaxAmount(), 2) : 0; $contribution['tax_exclusive_amount'] = $contribution['total_amount'] - $contribution['tax_amount']; + $contribution['address_id.name'] = 'Barbara Johnson'; + $contribution['address_id.display'] = '790L Lincoln St S +Baltimore, New York 10545 +United States'; $messageTemplate->setContribution($contribution); $messageTemplate->setOrder($mockOrder); $messageTemplate->setParticipantContacts($participantContacts); diff --git a/CRM/Utils/Address.php b/CRM/Utils/Address.php index 3e9b4fe0de..aa0703e518 100644 --- a/CRM/Utils/Address.php +++ b/CRM/Utils/Address.php @@ -90,11 +90,11 @@ class CRM_Utils_Address { 'contact.supplemental_address_2' => $fields['supplemental_address_2'] ?? NULL, 'contact.supplemental_address_3' => $fields['supplemental_address_3'] ?? NULL, 'contact.city' => $fields['city'] ?? NULL, - 'contact.state_province_name' => $fields['state_province_name'] ?? NULL, - 'contact.county' => $fields['county'] ?? NULL, - 'contact.state_province' => $fields['state_province'] ?? NULL, + 'contact.state_province_name' => $fields['state_province_id:label'] ?? ($fields['state_province_name'] ?? NULL), + 'contact.county' => $fields['county_id:label'] ?? ($fields['county'] ?? NULL), + 'contact.state_province' => $fields['state_province_id:label'] ?? ($fields['state_province'] ?? NULL), 'contact.postal_code' => $fullPostalCode, - 'contact.country' => $fields['country'] ?? NULL, + 'contact.country' => $fields['country_id:label'] ?? ($fields['country'] ?? NULL), 'contact.world_region' => $fields['world_region'] ?? NULL, 'contact.geo_code_1' => $fields['geo_code_1'] ?? NULL, 'contact.geo_code_2' => $fields['geo_code_2'] ?? NULL, diff --git a/tests/phpunit/CRM/Contribute/ActionMapping/ByTypeTest.php b/tests/phpunit/CRM/Contribute/ActionMapping/ByTypeTest.php index 45fc72ef46..5387f6c103 100644 --- a/tests/phpunit/CRM/Contribute/ActionMapping/ByTypeTest.php +++ b/tests/phpunit/CRM/Contribute/ActionMapping/ByTypeTest.php @@ -447,6 +447,9 @@ class CRM_Contribute_ActionMapping_ByTypeTest extends AbstractMappingTest { 'contribution_page_id.pay_later_text' => 'Pay Later Text', 'contribution_page_id.pay_later_receipt' => 'Pay Later Receipt', 'contribution_page_id.receipt_text' => 'Receipt Text', + 'address_id.id' => 'Address ID', + 'address_id.name' => 'Billing Address Name', + 'address_id.display' => 'Billing Address', ], $comparison); } diff --git a/tests/phpunit/CRM/Event/Form/ParticipantTest.php b/tests/phpunit/CRM/Event/Form/ParticipantTest.php index 12b13ea5cd..114b205404 100644 --- a/tests/phpunit/CRM/Event/Form/ParticipantTest.php +++ b/tests/phpunit/CRM/Event/Form/ParticipantTest.php @@ -217,7 +217,13 @@ class CRM_Event_Form_ParticipantTest extends CiviUnitTestCase { $this->setCurrencySeparators($thousandSeparator); $_REQUEST['mode'] = 'live'; $paymentProcessorID = $this->processorCreate(['is_test' => 0]); - $this->submitForm(['is_monetary' => 1, 'financial_type_id' => 1], $this->getSubmitParamsForCreditCardPayment($paymentProcessorID), TRUE); + $form = $this->submitForm(['is_monetary' => 1, 'financial_type_id' => 1], $this->getSubmitParamsForCreditCardPayment($paymentProcessorID), TRUE); + $this->assertStringContainsStrings($form->getFirstMailBody(), [ + 'Junko Adams
', + '790L Lincoln St S
+Baltimore, New York 10545
+United States
', + ]); $participant = $this->callAPISuccessGetSingle('Participant', []); $this->assertEquals('2018-09-04 00:00:00', $participant['participant_register_date']); $this->assertEquals('Offline Registration for Event: Annual CiviCRM meet by: ', $participant['participant_source']); diff --git a/tests/phpunit/CRM/Utils/TokenConsistencyTest.php b/tests/phpunit/CRM/Utils/TokenConsistencyTest.php index 6f1716050c..f8311a59d1 100644 --- a/tests/phpunit/CRM/Utils/TokenConsistencyTest.php +++ b/tests/phpunit/CRM/Utils/TokenConsistencyTest.php @@ -281,7 +281,29 @@ contribution.contribution_page_id.receipt_text :Text in 'locale' => 'nb_NO', ]); $tokenProcessor->evaluate(); - $this->assertEquals('€ 5 990,99', $tokenProcessor->getRow(0)->render('html')); + $this->assertEquals('€ 5 990,99', $tokenProcessor->getRow(0) + ->render('html')); + } + + /** + * Test various contribution tokens. + * + * There is additional testing for contribution tokens in CRM_Contribute_ActionMapping_ByTypeTest + * so this does not attempt to be complete. + */ + public function testContributionTokens(): void { + $this->createTestEntity('Address', ['name' => 'Bob Smith', 'street_address' => '123 Sesame Street', 'country_id' => 1058, 'supplemental_address_1' => 'The End']); + $contributionID = $this->contributionCreate(['contact_id' => $this->individualCreate(), 'address_id' => $this->ids['Address']['default']]); + $tokenString = '{contribution.address_id.name} ---- {contribution.address_id.display}'; + $html = $this->renderText(['contributionId' => $contributionID], $tokenString); + $this->assertEquals('Bob Smith ---- 123 Sesame Street
+The End
+Czech Republic
', $html); + $text = $this->renderText(['contributionId' => $contributionID], $tokenString, [], FALSE); + $this->assertEquals('Bob Smith ---- 123 Sesame Street +The End +Czech Republic +', $text); } /** @@ -1165,17 +1187,18 @@ Attendees will need to install the [TeleFoo](http://telefoo.example.com) app.'; * @param array $rowContext * @param string $text * @param array $context + * @param bool $isHtml * * @return string */ - protected function renderText(array $rowContext, string $text, array $context = []): string { + protected function renderText(array $rowContext, string $text, array $context = [], $isHtml = TRUE): string { $context['schema'] = $context['schema'] ?? []; foreach (array_keys($rowContext) as $key) { $context['schema'][] = $key; } $tokenProcessor = $this->getTokenProcessor($context); $tokenProcessor->addRow($rowContext); - $tokenProcessor->addMessage('text', $text, 'text/html'); + $tokenProcessor->addMessage('text', $text, 'text/' . ($isHtml ? 'html' : 'plain')); $tokenProcessor->evaluate(); return $tokenProcessor->getRow(0)->render('text'); } diff --git a/tests/templates/message_templates/event_online_receipt_text.tpl b/tests/templates/message_templates/event_online_receipt_text.tpl index 80bdb64bb4..502b901255 100644 --- a/tests/templates/message_templates/event_online_receipt_text.tpl +++ b/tests/templates/message_templates/event_online_receipt_text.tpl @@ -98,9 +98,7 @@ paidBy:::{$paidBy} {if isset($checkNumber)} checkNumber:::{$checkNumber} {/if} -{if isset($billingName)} -billingName:::{$billingName} -{/if} +billingName:::{contribution.address_id.name} {if isset($credit_card_type)} credit_card_type:::{$credit_card_type} credit_card_number:::{$credit_card_number} diff --git a/xml/templates/message_templates/event_offline_receipt_html.tpl b/xml/templates/message_templates/event_offline_receipt_html.tpl index afcfbbc115..a369da749c 100644 --- a/xml/templates/message_templates/event_offline_receipt_html.tpl +++ b/xml/templates/message_templates/event_offline_receipt_html.tpl @@ -368,7 +368,7 @@ {/if} - {if !empty($billingName)} + {if {contribution.address_id.display|boolean}} {ts}Billing Name and Address{/ts} @@ -376,8 +376,8 @@ - {$billingName}
- {$address|nl2br} + {contribution.address_id.name}
+ {contribution.address_id.display} {/if} diff --git a/xml/templates/message_templates/event_offline_receipt_text.tpl b/xml/templates/message_templates/event_offline_receipt_text.tpl index 62bb785446..086fa4c3ad 100644 --- a/xml/templates/message_templates/event_offline_receipt_text.tpl +++ b/xml/templates/message_templates/event_offline_receipt_text.tpl @@ -192,7 +192,7 @@ {if {contribution.check_number|boolean}} {ts}Check Number{/ts}: {contribution.check_number} {/if} -{if !empty($billingName)} +{if {contribution.address_id.display|boolean}} =============================================================================== @@ -200,8 +200,8 @@ =============================================================================== -{$billingName} -{$address} +{contribution.address_id.name} +{contribution.address_id.display} {/if} {if !empty($credit_card_type)} diff --git a/xml/templates/message_templates/event_online_receipt_html.tpl b/xml/templates/message_templates/event_online_receipt_html.tpl index 12b3be3e4e..30b451ca27 100644 --- a/xml/templates/message_templates/event_online_receipt_html.tpl +++ b/xml/templates/message_templates/event_online_receipt_html.tpl @@ -389,7 +389,7 @@ {/if} - {if !empty($billingName)} + {if {contribution.address_id.display|boolean}} {ts}Billing Name and Address{/ts} @@ -397,8 +397,8 @@ - {$billingName}
- {$address|nl2br} + {contribution.address_id.name}
+ {contribution.address_id.display} {/if} diff --git a/xml/templates/message_templates/event_online_receipt_text.tpl b/xml/templates/message_templates/event_online_receipt_text.tpl index d9adb6fab7..1eb6b86c06 100644 --- a/xml/templates/message_templates/event_online_receipt_text.tpl +++ b/xml/templates/message_templates/event_online_receipt_text.tpl @@ -184,7 +184,7 @@ You were registered by: {$payer.name} {if !empty($checkNumber)} {ts}Check Number{/ts}: {$checkNumber} {/if} -{if !empty($billingName)} +{if {contribution.address_id.display|boolean}} =============================================================================== @@ -192,8 +192,8 @@ You were registered by: {$payer.name} =============================================================================== -{$billingName} -{$address} +{contribution.address_id.name} +{contribution.address_id.display} {/if} {if !empty($credit_card_type)} -- 2.25.1