From 6946ef1aa18a9834b64c18625228cb85e308b370 Mon Sep 17 00:00:00 2001 From: Jon Waldstein Date: Thu, 14 Dec 2023 14:56:07 -0500 Subject: [PATCH 1/4] feature: add receipt api for fields --- .../FieldsAPI/Concerns/ShowInReceipt.php | 64 +++++++++++++++++++ .../GenerateConfirmationPageReceipt.php | 21 +++++- 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/src/Framework/FieldsAPI/Concerns/ShowInReceipt.php b/src/Framework/FieldsAPI/Concerns/ShowInReceipt.php index 13afa9240a..a3497add92 100644 --- a/src/Framework/FieldsAPI/Concerns/ShowInReceipt.php +++ b/src/Framework/FieldsAPI/Concerns/ShowInReceipt.php @@ -2,6 +2,8 @@ namespace Give\Framework\FieldsAPI\Concerns; +use Closure; + /** * @since 2.10.2 */ @@ -13,6 +15,16 @@ trait ShowInReceipt */ protected $showInReceipt = false; + /** + * @var string + */ + protected $receiptLabel; + + /** + * @var Closure + */ + protected $receiptValueCallback; + /** * @since 2.10.2 */ @@ -30,4 +42,56 @@ public function shouldShowInReceipt(): bool { return $this->showInReceipt; } + + /** + * @unreleased + */ + public function receiptLabel(string $label): self + { + $this->receiptLabel = $label; + + return $this; + } + + /** + * @unreleased + */ + public function hasReceiptLabel(): bool + { + return !empty($this->receiptLabel); + } + + /** + * @unreleased + */ + public function getReceiptLabel(): string + { + return $this->receiptLabel; + } + + /** + * @unreleased + */ + public function hasReceiptValue(): bool + { + return !empty($this->receiptValueCallback); + } + + /** + * @unreleased + */ + public function getReceiptValue(): Closure + { + return $this->receiptValueCallback; + } + + /** + * @unreleased + */ + public function receiptValue(Closure $value): self + { + $this->receiptValueCallback = $value; + + return $this; + } } diff --git a/src/Framework/Receipts/Actions/GenerateConfirmationPageReceipt.php b/src/Framework/Receipts/Actions/GenerateConfirmationPageReceipt.php index 5d5e781ea2..26a9ebb7f5 100644 --- a/src/Framework/Receipts/Actions/GenerateConfirmationPageReceipt.php +++ b/src/Framework/Receipts/Actions/GenerateConfirmationPageReceipt.php @@ -12,6 +12,7 @@ use Give\Framework\Receipts\DonationReceipt; use Give\Framework\Receipts\Properties\ReceiptDetail; use Give\Framework\TemplateTags\DonationTemplateTags; +use Give\Log\Log; class GenerateConfirmationPageReceipt { @@ -30,6 +31,7 @@ public function __invoke(DonationReceipt $receipt): DonationReceipt } /** + * @unreleased added support for retrieving custom fields programmatically with Fields API * @since 3.0.0 */ protected function getCustomFields(Donation $donation): array @@ -48,7 +50,20 @@ protected function getCustomFields(Donation $donation): array $receiptDetails = []; foreach($customFields as $field) { /** @var Field|HasLabel|HasName $field */ - if ($field->shouldStoreAsDonorMeta()) { + if ($field->hasReceiptValue()) { + try { + $value = $field->getReceiptValue()($field, $donation); + } catch (\Exception $e) { + $value = null; + + Log::error('Error getting receipt value for field', [ + 'field' => $field->getName(), + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString(), + ]); + } + + } elseif ($field->shouldStoreAsDonorMeta()) { if (!metadata_exists('donor', $donation->donor->id, $field->getName()) ) { continue; } @@ -63,8 +78,8 @@ protected function getCustomFields(Donation $donation): array } $receiptDetails[] = new ReceiptDetail( - $field->getLabel(), - $value + $field->hasReceiptLabel() ? $field->getReceiptLabel() : $field->getLabel(), + $value ?? '' ); } From b26396e0306b498695038f868a58c32587c99fd1 Mon Sep 17 00:00:00 2001 From: Jon Waldstein Date: Thu, 14 Dec 2023 15:25:36 -0500 Subject: [PATCH 2/4] tests: add ShowInReceiptTest --- .../FieldsAPI/Concerns/ShowInReceiptTest.php | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 tests/Unit/Framework/FieldsAPI/Concerns/ShowInReceiptTest.php diff --git a/tests/Unit/Framework/FieldsAPI/Concerns/ShowInReceiptTest.php b/tests/Unit/Framework/FieldsAPI/Concerns/ShowInReceiptTest.php new file mode 100644 index 0000000000..06667aed0e --- /dev/null +++ b/tests/Unit/Framework/FieldsAPI/Concerns/ShowInReceiptTest.php @@ -0,0 +1,56 @@ +getMockForTrait(ShowInReceipt::class); + + $mock->receiptLabel('test'); + $this->assertTrue($mock->hasReceiptLabel()); + $this->assertSame('test', $mock->getReceiptLabel()); + } + + /** + * @unreleased + */ + public function testSettingTheReceiptValue(): void + { + /** @var ShowInReceipt $mock */ + $mock = $this->getMockForTrait(ShowInReceipt::class); + + $value = function ($field, $donation) { + return 'test'; + }; + + $mock->receiptValue($value); + $this->assertTrue($mock->hasReceiptValue()); + self::assertIsCallable($mock->getReceiptValue()); + $this->assertSame($value, $mock->getReceiptValue()); + } + + /** + * @unreleased + */ + public function testShouldShowInReceipt(): void + { + /** @var ShowInReceipt $mock */ + $mock = $this->getMockForTrait(ShowInReceipt::class); + + $mock->showInReceipt(); + $this->assertTrue($mock->shouldShowInReceipt()); + } +} From 097c2db6ce160bf2f1f85cbc0d28ea1901345c50 Mon Sep 17 00:00:00 2001 From: Jon Waldstein Date: Thu, 14 Dec 2023 15:38:03 -0500 Subject: [PATCH 3/4] tests: add test to generate action --- .../TestGenerateConfirmationPageReceipt.php | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/Unit/Framework/Receipts/TestGenerateConfirmationPageReceipt.php b/tests/Unit/Framework/Receipts/TestGenerateConfirmationPageReceipt.php index 27bcb80b80..5a50f77fb5 100644 --- a/tests/Unit/Framework/Receipts/TestGenerateConfirmationPageReceipt.php +++ b/tests/Unit/Framework/Receipts/TestGenerateConfirmationPageReceipt.php @@ -4,6 +4,7 @@ use Give\DonationForms\Models\DonationForm; use Give\Donations\Models\Donation; +use Give\Framework\FieldsAPI\Text; use Give\Framework\Receipts\Actions\GenerateConfirmationPageReceipt; use Give\Framework\Receipts\Properties\ReceiptDetail; use Give\Framework\Receipts\Properties\ReceiptDetailCollection; @@ -247,4 +248,51 @@ public function testShouldGenerateReceiptForRecurringDonation() ] ); } + + /** + * @unreleased + */ + public function testShouldAddCustomFieldsToAdditionalDetails(): void + { + $field = Text::make('favorite_color') + ->showInAdmin() + ->defaultValue('Blue') + ->scope('custom_scope') + ->showInReceipt() + ->receiptLabel('Your favorite color:') + ->receiptValue(static function (Text $field, $donation) { + return $field->getDefaultValue(); + }) + ; + + add_action('givewp_donation_form_schema', static function (\Give\Framework\FieldsAPI\DonationForm $form) use ($field) { + $form->insertAfter('email', $field); + }); + + /** @var DonationForm $donationForm */ + $donationForm = DonationForm::factory()->create(); + + /** @var Donation $donation */ + $donation = Donation::factory()->create([ + 'formId' => $donationForm->id + ]); + + $initialReceipt = new DonationReceipt($donation); + + $receipt = (new GenerateConfirmationPageReceipt())($initialReceipt); + + $additionalDetails = new ReceiptDetailCollection(); + + $additionalDetails->addDetail( + new ReceiptDetail( + __('Your favorite color:', 'give'), + 'Blue' + ) + ); + + $this->assertContains( + $additionalDetails->toArray()[0], + $receipt->additionalDetails->toArray() + ); + } } From 699e681a918efe9ff976fcfd54793a8f3d672eaf Mon Sep 17 00:00:00 2001 From: Jon Waldstein Date: Thu, 14 Dec 2023 16:15:25 -0500 Subject: [PATCH 4/4] refactor: update to explicitly check for scopes and add filters for good measure --- .../FieldsAPI/Concerns/ShowInReceipt.php | 8 ++++ .../GenerateConfirmationPageReceipt.php | 48 ++++++++++++++----- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/src/Framework/FieldsAPI/Concerns/ShowInReceipt.php b/src/Framework/FieldsAPI/Concerns/ShowInReceipt.php index a3497add92..c246bb0b4c 100644 --- a/src/Framework/FieldsAPI/Concerns/ShowInReceipt.php +++ b/src/Framework/FieldsAPI/Concerns/ShowInReceipt.php @@ -77,6 +77,14 @@ public function hasReceiptValue(): bool return !empty($this->receiptValueCallback); } + /** + * @unreleased + */ + public function isReceiptValueCallback(): bool + { + return is_callable($this->receiptValueCallback); + } + /** * @unreleased */ diff --git a/src/Framework/Receipts/Actions/GenerateConfirmationPageReceipt.php b/src/Framework/Receipts/Actions/GenerateConfirmationPageReceipt.php index 26a9ebb7f5..a539a4c764 100644 --- a/src/Framework/Receipts/Actions/GenerateConfirmationPageReceipt.php +++ b/src/Framework/Receipts/Actions/GenerateConfirmationPageReceipt.php @@ -2,6 +2,7 @@ namespace Give\Framework\Receipts\Actions; +use Exception; use Give\DonationForms\Models\DonationForm; use Give\DonationForms\Repositories\DonationFormRepository; use Give\Donations\Models\Donation; @@ -31,7 +32,7 @@ public function __invoke(DonationReceipt $receipt): DonationReceipt } /** - * @unreleased added support for retrieving custom fields programmatically with Fields API + * @unreleased updated conditional to check for scopes and added support for retrieving values programmatically with Fields API * @since 3.0.0 */ protected function getCustomFields(Donation $donation): array @@ -48,12 +49,12 @@ protected function getCustomFields(Donation $donation): array }); $receiptDetails = []; - foreach($customFields as $field) { + foreach ($customFields as $field) { /** @var Field|HasLabel|HasName $field */ if ($field->hasReceiptValue()) { try { - $value = $field->getReceiptValue()($field, $donation); - } catch (\Exception $e) { + $value = $field->isReceiptValueCallback() ? $field->getReceiptValue()($field, $donation) : null; + } catch (Exception $e) { $value = null; Log::error('Error getting receipt value for field', [ @@ -62,25 +63,42 @@ protected function getCustomFields(Donation $donation): array 'trace' => $e->getTraceAsString(), ]); } - - } elseif ($field->shouldStoreAsDonorMeta()) { - if (!metadata_exists('donor', $donation->donor->id, $field->getName()) ) { + } elseif ($field->getScope()->isDonor()) { + if (!metadata_exists('donor', $donation->donor->id, $field->getName())) { continue; } $value = give()->donor_meta->get_meta($donation->donor->id, $field->getName(), true); - } else { - if (!metadata_exists('donation', $donation->id, $field->getName()) ) { + } elseif ($field->getScope()->isDonation()) { + if (!metadata_exists('donation', $donation->id, $field->getName())) { continue; } $value = give()->payment_meta->get_meta($donation->id, $field->getName(), true); + } else { + $value = null; } - $receiptDetails[] = new ReceiptDetail( + $value = apply_filters( + sprintf("givewp_donation_confirmation_page_field_value_for_%s", $field->getName()), + $value, + $field, + $donation + ); + + $label = apply_filters( + sprintf("givewp_donation_confirmation_page_field_label_for_%s", $field->getName()), $field->hasReceiptLabel() ? $field->getReceiptLabel() : $field->getLabel(), - $value ?? '' + $field, + $donation ); + + if (!empty($label)){ + $receiptDetails[] = new ReceiptDetail( + $label, + $value ?? '' + ); + } } return $receiptDetails; @@ -98,7 +116,10 @@ private function fillDonationDetails(DonationReceipt $receipt) $paymentMethodLabel = give_get_gateway_checkout_label($receipt->donation->gatewayId, null); if (empty($paymentMethodLabel) || $paymentMethodLabel === $receipt->donation->gatewayId) { - $paymentMethodLabel = $paymentGatewayRegistrar->hasPaymentGateway($receipt->donation->gatewayId) ? $paymentGatewayRegistrar->getPaymentGateway($receipt->donation->gatewayId)->getPaymentMethodLabel() : $receipt->donation->gatewayId; + $paymentMethodLabel = $paymentGatewayRegistrar->hasPaymentGateway( + $receipt->donation->gatewayId + ) ? $paymentGatewayRegistrar->getPaymentGateway($receipt->donation->gatewayId)->getPaymentMethodLabel( + ) : $receipt->donation->gatewayId; } $receipt->donationDetails->addDetails([ @@ -157,7 +178,8 @@ private function fillDonorDetails(DonationReceipt $receipt) __('Billing Address', 'give'), $receipt->donation->billingAddress->address1 . ' ' . $receipt->donation->billingAddress->address2 . PHP_EOL . $receipt->donation->billingAddress->city . ($receipt->donation->billingAddress->state ? ', ' . $receipt->donation->billingAddress->state : '') . ' ' . $receipt->donation->billingAddress->zip . PHP_EOL . - $receipt->donation->billingAddress->country . PHP_EOL); + $receipt->donation->billingAddress->country . PHP_EOL + ); } $receipt->donorDetails->addDetails($details);