From 0f6ddab4f0276d2fb4cf8fec0c01756ad7c5c1b1 Mon Sep 17 00:00:00 2001 From: Can Demiralp Date: Thu, 27 Feb 2025 11:38:44 +0100 Subject: [PATCH] [ECP-8888] Implement LineItemsDataBuilder and add lineItems to PayPal requests (#2892) * [ECP-8888] Implement new data builder for lineItems and set new fields in the lineItems array * [ECP-8888] Use isOpenInvoice() method to determine the open invoice characteristic of the PM * [ECP-8888] Update PHPDocs * [ECP-8888] Remove unused import * [ECP-8888] Update the condition and remove unnecessary cast * [ECP-8888] Update unit tests * [ECP-8888] Fix maintainability issue * [ECP-8888] Write unit tests * [ECP-8888] Write unit tests * [ECP-8888] Remove lineItems builder from the CheckoutDataBuilder * [ECP-8888] Check the requirement of lineItems based on the new method * [ECP-8888] Set itemCategory field only for PayPal * [ECP-8888] Fix unit failing tests * [ECP-8888] Update the log message * [ECP-8888] Write unit tests --------- Co-authored-by: Can Demiralp --- Gateway/Http/Client/TransactionPayment.php | 1 - Gateway/Request/CaptureDataBuilder.php | 5 +- Gateway/Request/CheckoutDataBuilder.php | 5 +- Gateway/Request/LineItemsDataBuilder.php | 61 +++++++++ Gateway/Request/RefundDataBuilder.php | 2 +- Helper/OpenInvoice.php | 67 +++++++-- Helper/PaymentMethods.php | 16 +++ .../Request/LineItemsDataBuilderTest.php | 111 +++++++++++++++ Test/Unit/Helper/OpenInvoiceTest.php | 128 +++++++++++++++--- Test/Unit/Helper/PaymentMethodsTest.php | 43 +++++- etc/config.xml | 1 + etc/di.xml | 3 + 12 files changed, 407 insertions(+), 36 deletions(-) create mode 100644 Gateway/Request/LineItemsDataBuilder.php create mode 100644 Test/Unit/Gateway/Request/LineItemsDataBuilderTest.php diff --git a/Gateway/Http/Client/TransactionPayment.php b/Gateway/Http/Client/TransactionPayment.php index 57d6474f86..1561c31fb5 100644 --- a/Gateway/Http/Client/TransactionPayment.php +++ b/Gateway/Http/Client/TransactionPayment.php @@ -15,7 +15,6 @@ use Adyen\Client; use Adyen\ConnectionException; use Adyen\Model\Checkout\PaymentRequest; -use Adyen\Model\Checkout\PaymentResponse as CheckoutPaymentResponse; use Adyen\Payment\Helper\Data; use Adyen\Payment\Helper\GiftcardPayment; use Adyen\Payment\Helper\Idempotency; diff --git a/Gateway/Request/CaptureDataBuilder.php b/Gateway/Request/CaptureDataBuilder.php index b2dda81d27..0a7671b9e4 100644 --- a/Gateway/Request/CaptureDataBuilder.php +++ b/Gateway/Request/CaptureDataBuilder.php @@ -111,8 +111,7 @@ public function build(array $buildSubject): array ] ]; - //Check additionaldata - if ($this->paymentMethodsHelper->isOpenInvoice($paymentMethodInstance)) { + if ($this->paymentMethodsHelper->getRequiresLineItems($paymentMethodInstance)) { $openInvoiceFields = $this->openInvoiceHelper->getOpenInvoiceDataForInvoice($latestInvoice); $requestBody = array_merge($requestBody, $openInvoiceFields); } @@ -171,7 +170,7 @@ public function buildPartialOrMultipleCaptureData($payment, $currency, $adyenOrd "paymentPspReference" => $adyenOrderPayment[OrderPaymentInterface::PSPREFRENCE] ]; - if ($this->paymentMethodsHelper->isOpenInvoice($paymentMethodInstance)) { + if ($this->paymentMethodsHelper->getRequiresLineItems($paymentMethodInstance)) { $order = $payment->getOrder(); $invoices = $order->getInvoiceCollection(); // The latest invoice will contain only the selected items(and quantities) for the (partial) capture diff --git a/Gateway/Request/CheckoutDataBuilder.php b/Gateway/Request/CheckoutDataBuilder.php index c6f8679643..538ceebf13 100644 --- a/Gateway/Request/CheckoutDataBuilder.php +++ b/Gateway/Request/CheckoutDataBuilder.php @@ -101,9 +101,6 @@ public function build(array $buildSubject): array $this->paymentMethodsHelper->isOpenInvoice($paymentMethodInstance) || $payment->getMethod() === AdyenPayByLinkConfigProvider::CODE ) { - $openInvoiceFields = $this->openInvoiceHelper->getOpenInvoiceDataForOrder($order); - $requestBody = array_merge($requestBody, $openInvoiceFields); - if (isset($brandCode) && $this->adyenHelper->isPaymentMethodOfType($brandCode, Data::KLARNA) && $this->configHelper->getAutoCaptureOpenInvoice($storeId)) { @@ -231,6 +228,8 @@ protected function getImageUrl($item): string } /** + * @deprecated Use Adyen\Payment\Helper\OpenInvoice::getOpenInvoiceDataForOrder() instead. + * * @param Order $order * * @return array diff --git a/Gateway/Request/LineItemsDataBuilder.php b/Gateway/Request/LineItemsDataBuilder.php new file mode 100644 index 0000000000..c33a8f08d2 --- /dev/null +++ b/Gateway/Request/LineItemsDataBuilder.php @@ -0,0 +1,61 @@ + + */ + +namespace Adyen\Payment\Gateway\Request; + +use Adyen\Payment\Helper\OpenInvoice; +use Adyen\Payment\Helper\PaymentMethods; +use Magento\Framework\Exception\LocalizedException; +use Magento\Payment\Gateway\Data\PaymentDataObject; +use Magento\Payment\Gateway\Helper\SubjectReader; +use Magento\Payment\Gateway\Request\BuilderInterface; +use Magento\Sales\Model\Order; + +class LineItemsDataBuilder implements BuilderInterface +{ + /** + * @param PaymentMethods $paymentMethodsHelper + * @param OpenInvoice $openInvoiceHelper + */ + public function __construct( + private readonly PaymentMethods $paymentMethodsHelper, + private readonly OpenInvoice $openInvoiceHelper + ) { } + + /** + * Add `lineItems` to the request if the payment method requires this field + * + * @param array $buildSubject + * @return array + * @throws LocalizedException + */ + public function build(array $buildSubject): array + { + /** @var PaymentDataObject $paymentDataObject */ + $paymentDataObject = SubjectReader::readPayment($buildSubject); + $payment = $paymentDataObject->getPayment(); + $paymentMethodInstance = $payment->getMethodInstance(); + /** @var Order $order */ + $order = $payment->getOrder(); + + $requestBody = []; + + $isLineItemsRequired = $this->paymentMethodsHelper->getRequiresLineItems($paymentMethodInstance); + if ($isLineItemsRequired === true) { + $requestLineItems = $this->openInvoiceHelper->getOpenInvoiceDataForOrder($order); + $requestBody = array_merge($requestBody, $requestLineItems); + } + + return [ + 'body' => $requestBody + ]; + } +} diff --git a/Gateway/Request/RefundDataBuilder.php b/Gateway/Request/RefundDataBuilder.php index 3cfe64b006..295841e16a 100644 --- a/Gateway/Request/RefundDataBuilder.php +++ b/Gateway/Request/RefundDataBuilder.php @@ -167,7 +167,7 @@ public function build(array $buildSubject): array ] ]; - if ($this->paymentMethodsHelper->isOpenInvoice($paymentMethodInstance)) { + if ($this->paymentMethodsHelper->getRequiresLineItems($paymentMethodInstance)) { $openInvoiceFieldsCreditMemo = $this->openInvoiceHelper->getOpenInvoiceDataForCreditMemo($creditMemo); //There is only one payment, so we add the fields to the first(and only) result $requestBody[0] = array_merge($requestBody[0], $openInvoiceFieldsCreditMemo); diff --git a/Helper/OpenInvoice.php b/Helper/OpenInvoice.php index f0d9439efe..7b133a0993 100644 --- a/Helper/OpenInvoice.php +++ b/Helper/OpenInvoice.php @@ -12,41 +12,53 @@ namespace Adyen\Payment\Helper; +use Adyen\Payment\Logger\AdyenLogger; use Adyen\Payment\Model\AdyenAmountCurrency; +use Exception; use Magento\Catalog\Helper\Image; use Magento\Quote\Api\CartRepositoryInterface; use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Item as OrderItem; use Magento\Sales\Model\Order\Invoice; -use Magento\Sales\Model\Order\Invoice\Item; +use Magento\Sales\Model\Order\Invoice\Item as InvoiceItem; +use Magento\Sales\Model\Order\Creditmemo; +use Magento\Sales\Model\Order\Creditmemo\Item as CreditmemoItem; use Magento\Quote\Model\Quote; +use Magento\Quote\Model\Quote\Item as QuoteItem; class OpenInvoice { + const ITEM_CATEGORY_DIGITAL_GOODS = 'DIGITAL_GOODS'; + const ITEM_CATEGORY_PHYSICAL_GOODS = 'PHYSICAL_GOODS'; + protected Data $adyenHelper; protected CartRepositoryInterface $cartRepository; protected ChargedCurrency $chargedCurrency; protected Config $configHelper; protected Image $imageHelper; + protected AdyenLogger $adyenLogger; public function __construct( - Data $adyenHelper, + Data $adyenHelper, CartRepositoryInterface $cartRepository, - ChargedCurrency $chargedCurrency, - Config $configHelper, - Image $imageHelper + ChargedCurrency $chargedCurrency, + Config $configHelper, + Image $imageHelper, + AdyenLogger $adyenLogger ) { $this->adyenHelper = $adyenHelper; $this->cartRepository = $cartRepository; $this->chargedCurrency = $chargedCurrency; $this->configHelper = $configHelper; $this->imageHelper = $imageHelper; + $this->adyenLogger = $adyenLogger; } public function getOpenInvoiceDataForInvoice(Invoice $invoice): array { $formFields = ['lineItems' => []]; - /* @var Item $invoiceItem */ + /* @var InvoiceItem $invoiceItem */ foreach ($invoice->getItems() as $invoiceItem) { $numberOfItems = (int)$invoiceItem->getQty(); $orderItem = $invoiceItem->getOrderItem(); @@ -92,10 +104,11 @@ public function getOpenInvoiceDataForOrder(Order $order): array return $formFields; } - public function getOpenInvoiceDataForCreditMemo(Order\Creditmemo $creditMemo) + public function getOpenInvoiceDataForCreditMemo(Creditmemo $creditMemo): array { $formFields = ['lineItems' => []]; + /* @var CreditmemoItem $creditmemoItem */ foreach ($creditMemo->getItems() as $creditmemoItem) { // Child items only identifies the variant data and doesn't contain line item information. $isChildItem = $creditmemoItem->getOrderItem()->getParentItem() !== null; @@ -172,16 +185,52 @@ protected function formatLineItem(AdyenAmountCurrency $itemAmountCurrency, $item $product = $item->getProduct(); - return [ + $lineItem = [ 'id' => $product ? $product->getId() : $item->getProductId(), 'amountIncludingTax' => $formattedPriceIncludingTax, + 'amountExcludingTax' => $formattedPriceIncludingTax - $formattedTaxAmount, 'taxAmount' => $formattedTaxAmount, + 'taxPercentage' => $formattedTaxPercentage, 'description' => $item->getName(), + 'sku' => $item->getSku(), 'quantity' => (int) ($qty ?? $item->getQty()), - 'taxPercentage' => $formattedTaxPercentage, 'productUrl' => $product ? $product->getUrlModel()->getUrl($product) : '', 'imageUrl' => $this->getImageUrl($item) ]; + + if ($itemCategory = $this->buildItemCategory($item)) { + $lineItem['itemCategory'] = $itemCategory; + } + + return $lineItem; + } + + /** + * Builds the `itemCategory` field required for PayPal + * + * @param QuoteItem|OrderItem $item + * @return string|null + */ + private function buildItemCategory($item): ?string + { + try { + if ($item instanceof QuoteItem) { + $paymentMethod = $item->getQuote()->getPayment()->getMethod(); + } elseif ($item instanceof OrderItem) { + $paymentMethod = $item->getOrder()->getPayment()->getMethod(); + } + + if (isset($paymentMethod) && strcmp($paymentMethod, PaymentMethods::ADYEN_PAYPAL) === 0) { + $isVirtual = boolval($item->getIsVirtual()); + $category = $isVirtual ? self::ITEM_CATEGORY_DIGITAL_GOODS : self::ITEM_CATEGORY_PHYSICAL_GOODS; + } + } catch (Exception $e) { + $this->adyenLogger->error( + __('An error occurred while building the line item field `itemCategory`: %1', $e->getMessage()) + ); + } + + return $category ?? null; } /** diff --git a/Helper/PaymentMethods.php b/Helper/PaymentMethods.php index 5c8b30f62e..ee1f0f2831 100644 --- a/Helper/PaymentMethods.php +++ b/Helper/PaymentMethods.php @@ -53,6 +53,7 @@ class PaymentMethods extends AbstractHelper const ADYEN_CC = 'adyen_cc'; const ADYEN_ONE_CLICK = 'adyen_oneclick'; const ADYEN_PAY_BY_LINK = 'adyen_pay_by_link'; + const ADYEN_PAYPAL = 'adyen_paypal'; const ADYEN_PREFIX = 'adyen_'; const ADYEN_CC_VAULT = 'adyen_cc_vault'; const METHODS_WITH_BRAND_LOGO = [ @@ -66,6 +67,7 @@ class PaymentMethods extends AbstractHelper const FUNDING_SOURCE_CREDIT = 'credit'; const ADYEN_GROUP_ALTERNATIVE_PAYMENT_METHODS = 'adyen-alternative-payment-method'; + const CONFIG_FIELD_REQUIRES_LINE_ITEMS = 'requires_line_items'; const CONFIG_FIELD_IS_OPEN_INVOICE = 'is_open_invoice'; const VALID_CHANNELS = ["iOS", "Android", "Web"]; @@ -1081,4 +1083,18 @@ public function isOpenInvoice(MethodInterface $paymentMethodInstance): bool { return boolval($paymentMethodInstance->getConfigData(self::CONFIG_FIELD_IS_OPEN_INVOICE)); } + + /** + * Checks the requirement of line items for the given payment method + * + * @param MethodInterface $paymentMethodInstance + * @return bool + */ + public function getRequiresLineItems(MethodInterface $paymentMethodInstance): bool + { + $isOpenInvoice = $this->isOpenInvoice($paymentMethodInstance); + $requiresLineItemsConfig = boolval($paymentMethodInstance->getConfigData(self::CONFIG_FIELD_REQUIRES_LINE_ITEMS)); + + return $isOpenInvoice || $requiresLineItemsConfig; + } } diff --git a/Test/Unit/Gateway/Request/LineItemsDataBuilderTest.php b/Test/Unit/Gateway/Request/LineItemsDataBuilderTest.php new file mode 100644 index 0000000000..0bd837fb62 --- /dev/null +++ b/Test/Unit/Gateway/Request/LineItemsDataBuilderTest.php @@ -0,0 +1,111 @@ + + */ + +namespace Adyen\Payment\Test\Gateway\Request; + +use Adyen\Payment\Gateway\Request\LineItemsDataBuilder; +use Adyen\Payment\Helper\OpenInvoice; +use Adyen\Payment\Helper\PaymentMethods; +use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; +use Magento\Framework\Exception\LocalizedException; +use Magento\Payment\Gateway\Data\PaymentDataObject; +use Magento\Payment\Model\MethodInterface; +use Magento\Sales\Model\Order; +use PHPUnit\Framework\MockObject\MockObject; + +class LineItemsDataBuilderTest extends AbstractAdyenTestCase +{ + protected ?LineItemsDataBuilder $lineItemsDataBuilder; + protected PaymentMethods|MockObject $paymentMethodsHelperMock; + protected OpenInvoice|MockObject $openInvoiceHelperMock; + + /** + * @return void + */ + protected function setUp(): void + { + $this->paymentMethodsHelperMock = $this->createMock(PaymentMethods::class); + $this->openInvoiceHelperMock = $this->createMock(OpenInvoice::class); + + $this->lineItemsDataBuilder = new LineItemsDataBuilder( + $this->paymentMethodsHelperMock, + $this->openInvoiceHelperMock + ); + } + + /** + * @return void + */ + protected function tearDown(): void + { + $this->lineItemsDataBuilder = null; + } + + /** + * @return array + */ + private static function buildDataProvider(): array + { + return [ + ['isLineItemsRequired' => true], + ['isLineItemsRequired' => false] + ]; + } + + /** + * @dataProvider buildDataProvider() + * + * @param bool $isLineItemsRequired + * @return void + * @throws LocalizedException + */ + public function testBuild(bool $isLineItemsRequired) + { + $orderMock = $this->createMock(Order::class); + + $paymentMethodInstanceMock = $this->createMock(MethodInterface::class); + + $paymentMock = $this->createMock(Order\Payment::class); + $paymentMock->expects($this->once()) + ->method('getOrder') + ->willReturn($orderMock); + $paymentMock->method('getMethodInstance')->willReturn($paymentMethodInstanceMock); + + $paymentDataObjectMock = $this->createMock(PaymentDataObject::class); + $paymentDataObjectMock->expects($this->once()) + ->method('getPayment') + ->willReturn($paymentMock); + + $this->paymentMethodsHelperMock->expects($this->once()) + ->method('getRequiresLineItems') + ->willReturn($isLineItemsRequired); + + if ($isLineItemsRequired) { + $this->openInvoiceHelperMock->expects($this->once()) + ->method('getOpenInvoiceDataForOrder') + ->with($orderMock) + ->willReturn(['lineItems' => [['id' => 1], ['id' => 2]]]); + } + + $buildSubject = ['payment' => $paymentDataObjectMock]; + $result = $this->lineItemsDataBuilder->build($buildSubject); + + $this->assertIsArray($result); + $this->assertArrayHasKey('body', $result); + + if ($isLineItemsRequired) { + $this->assertArrayHasKey('lineItems', $result['body']); + $this->assertArrayHasKey('id', $result['body']['lineItems'][0]); + } else { + $this->assertEmpty($result['body']); + } + } +} diff --git a/Test/Unit/Helper/OpenInvoiceTest.php b/Test/Unit/Helper/OpenInvoiceTest.php index cef2f89122..9a5949573b 100644 --- a/Test/Unit/Helper/OpenInvoiceTest.php +++ b/Test/Unit/Helper/OpenInvoiceTest.php @@ -2,14 +2,18 @@ namespace Adyen\Payment\Test\Unit\Helper; +use Adyen\AdyenException; use Adyen\Payment\Helper\ChargedCurrency; use Adyen\Payment\Helper\Config; use Adyen\Payment\Helper\Data; +use Adyen\Payment\Helper\PaymentMethods; +use Adyen\Payment\Logger\AdyenLogger; use Adyen\Payment\Model\AdyenAmountCurrency; use Adyen\Payment\Test\Unit\AbstractAdyenTestCase; use Adyen\Payment\Helper\OpenInvoice; use Magento\Catalog\Helper\Image; use Magento\Catalog\Model\Product; +use Magento\Framework\Exception\NotFoundException; use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Model\Quote; use Magento\Quote\Model\Quote\Address; @@ -17,6 +21,7 @@ use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Creditmemo; use Magento\Sales\Model\Order\Invoice; +use Magento\Sales\Model\Order\Payment; class OpenInvoiceTest extends AbstractAdyenTestCase { @@ -26,8 +31,11 @@ class OpenInvoiceTest extends AbstractAdyenTestCase private $configHelperMock; private $imageHelperMock; private $orderMock; + private $orderPaymentMock; + private $quoteMock; + private $quotePaymentMock; private $cartMock; - private $itemMock; + private $quoteItemMock; private $productMock; private $invoiceMock; private $orderItemMock; @@ -36,6 +44,7 @@ class OpenInvoiceTest extends AbstractAdyenTestCase private $creditmemoItemMock; private $shippingAddressMock; private $shippingAmountCurrencyMock; + private $adyenLoggerMock; protected function setUp(): void { @@ -47,27 +56,44 @@ protected function setUp(): void $this->imageHelperMock = $this->createMock(Image::class); # Other mock property definitions + $this->orderPaymentMock = $this->createMock(Payment::class); $this->orderMock = $this->createMock(Order::class); - $this->itemMock = $this->createMock(Item::class); + $this->orderMock->method('getPayment')->willReturn($this->orderPaymentMock); + $this->quotePaymentMock = $this->createMock(Quote\Payment::class); + $this->quoteMock = $this->createMock(Quote::class); + $this->quoteMock->method('getPayment')->willReturn($this->quotePaymentMock); + $this->quoteItemMock = $this->createGeneratedMock(Item::class, + ['getIsVirtual', 'getQty', 'getProduct', 'getName', 'getSku', 'getTaxPercent', 'getQuote'] + ); $this->productMock = $this->createMock(Product::class); $this->invoiceMock = $this->createMock(Invoice::class); - $this->orderItemMock = $this->createMock(Order\Item::class); + $this->orderItemMock = $this->createGeneratedMock(Order\Item::class, + ['getIsVirtual', 'getQty', 'getProduct', 'getName', 'getSku', 'getTaxPercent', 'getOrder'] + ); $this->invoiceItemMock = $this->createMock(Invoice\Item::class); $this->creditmemoMock = $this->createMock(Creditmemo::class); $this->creditmemoItemMock = $this->createMock(Creditmemo\Item::class); $this->cartMock = $this->createMock(Quote::class); $this->shippingAddressMock = $this->createMock(Address::class); $this->shippingAmountCurrencyMock = $this->createMock(AdyenAmountCurrency::class); + $this->adyenLoggerMock = $this->createMock(AdyenLogger::class); } - public function testGetOpenInvoiceDataFomOrder(): void + /** + * @dataProvider isVirtualDataProvider() + * + * @param bool $isVirtual + * @return void + */ + public function testGetOpenInvoiceDataForOrder(bool $isVirtual): void { $openInvoice = new OpenInvoice( $this->adyenHelperMock, $this->cartRepositoryMock, $this->chargedCurrencyMock, $this->configHelperMock, - $this->imageHelperMock + $this->imageHelperMock, + $this->adyenLoggerMock ); $itemAmountCurrencyMock = $this->createMock(AdyenAmountCurrency::class); @@ -77,12 +103,17 @@ public function testGetOpenInvoiceDataFomOrder(): void $this->chargedCurrencyMock->method('getQuoteItemAmountCurrency')->willReturn($itemAmountCurrencyMock); - $this->itemMock->method('getQty')->willReturn(1); - $this->itemMock->method('getProduct')->willReturn($this->productMock); - $this->itemMock->method('getName')->willReturn('Push It Messenger Bag'); + $this->quoteItemMock->method('getQty')->willReturn(1); + $this->quoteItemMock->method('getProduct')->willReturn($this->productMock); + $this->quoteItemMock->method('getName')->willReturn('Push It Messenger Bag'); + $this->quoteItemMock->method('getSku')->willReturn('24-WB04'); + $this->quoteItemMock->method('getIsVirtual')->willReturn($isVirtual); + $this->quoteItemMock->method('getQuote')->willReturn($this->quoteMock); + + $this->quotePaymentMock->method('getMethod')->willReturn(PaymentMethods::ADYEN_PAYPAL); $this->cartMock->method('getShippingAddress')->willReturn($this->shippingAddressMock); - $this->cartMock->method('getAllVisibleItems')->willReturn([$this->itemMock]); + $this->cartMock->method('getAllVisibleItems')->willReturn([$this->quoteItemMock]); $this->cartRepositoryMock->method('get')->willReturn($this->cartMock); @@ -118,12 +149,17 @@ public function getUrl() [ 'id' => '14', 'amountIncludingTax' => 10000, + 'amountExcludingTax' => 10000, 'taxAmount' => 0, + 'taxPercentage' => 0, 'description' => 'Push It Messenger Bag', + 'sku' => '24-WB04', 'quantity' => 1, - 'taxPercentage' => 0, 'productUrl' => 'https://localhost.store/index.php/push-it-messenger-bag.html', 'imageUrl' => '', + 'itemCategory' => $isVirtual ? + OpenInvoice::ITEM_CATEGORY_DIGITAL_GOODS : + OpenInvoice::ITEM_CATEGORY_PHYSICAL_GOODS ], [ 'id' => 'shippingCost', @@ -140,7 +176,38 @@ public function getUrl() $this->assertEquals($expectedResult, $result); } - public function testGetOpenInvoiceDataForLastInvoice(): void + /** + * @return void + */ + public function testBuildItemCategoryException(): void + { + $openInvoice = new OpenInvoice( + $this->adyenHelperMock, + $this->cartRepositoryMock, + $this->chargedCurrencyMock, + $this->configHelperMock, + $this->imageHelperMock, + $this->adyenLoggerMock + ); + + $itemAmountCurrencyMock = $this->createMock(AdyenAmountCurrency::class); + $this->chargedCurrencyMock->method('getQuoteItemAmountCurrency')->willReturn($itemAmountCurrencyMock); + $this->quoteItemMock->method('getQuote')->willThrowException(new AdyenException()); + $this->cartRepositoryMock->method('get')->willReturn($this->cartMock); + $this->cartMock->method('getAllVisibleItems')->willReturn([$this->quoteItemMock]); + $this->cartMock->method('getShippingAddress')->willReturn($this->shippingAddressMock); + + $result = $openInvoice->getOpenInvoiceDataForOrder($this->orderMock); + $this->assertArrayNotHasKey('itemCategory', $result); + } + + /** + * @dataProvider isVirtualDataProvider() + * + * @param bool $isVirtual + * @return void + */ + public function testGetOpenInvoiceDataForLastInvoice(bool $isVirtual): void { $itemAmountCurrencyMock = $this->createMock(AdyenAmountCurrency::class); $itemAmountCurrencyMock->method('getAmountIncludingTax')->willReturn(121.00); @@ -157,6 +224,9 @@ public function getUrl() $this->orderItemMock->method('getProduct')->willReturn($this->productMock); $this->orderItemMock->method('getName')->willReturn('Push It Messenger Bag'); $this->orderItemMock->method('getTaxPercent')->willReturn(21); + $this->orderItemMock->method('getSku')->willReturn('24-WB04'); + $this->orderItemMock->method('getIsVirtual')->willReturn($isVirtual); + $this->orderItemMock->method('getOrder')->willReturn($this->orderMock); $this->invoiceItemMock->method('getOrderItem')->willReturn($this->orderItemMock); $this->invoiceItemMock->method('getQty')->willReturn(1); @@ -178,7 +248,8 @@ public function getUrl() $this->cartRepositoryMock, $this->chargedCurrencyMock, $this->configHelperMock, - $this->imageHelperMock + $this->imageHelperMock, + $this->adyenLoggerMock ); $expectedResult = [ @@ -186,10 +257,12 @@ public function getUrl() [ 'id' => '14', 'amountIncludingTax' => 12100, + 'amountExcludingTax' => 10000, 'taxAmount' => 2100, + 'taxPercentage' => 2100, 'description' => 'Push It Messenger Bag', + 'sku' => '24-WB04', 'quantity' => 1, - 'taxPercentage' => 2100, 'productUrl' => 'https://localhost.store/index.php/push-it-messenger-bag.html', 'imageUrl' => '', ], @@ -208,7 +281,13 @@ public function getUrl() $this->assertEquals($expectedResult, $result); } - public function testGetOpenInvoiceDataForCreditMemo(): void + /** + * @dataProvider isVirtualDataProvider() + * + * @param bool $isVirtual + * @return void + */ + public function testGetOpenInvoiceDataForCreditMemo(bool $isVirtual): void { $this->creditmemoMock->method('getItems')->willReturn([$this->creditmemoItemMock]); $this->creditmemoMock->method('getShippingAmount')->willReturn(50); @@ -220,6 +299,9 @@ public function testGetOpenInvoiceDataForCreditMemo(): void $this->orderItemMock->method('getName')->willReturn('Push It Messenger Bag'); $this->orderItemMock->method('getProduct')->willReturn($this->productMock); $this->orderItemMock->method('getTaxPercent')->willReturn(0); + $this->orderItemMock->method('getSku')->willReturn('24-WB04'); + $this->orderItemMock->method('getIsVirtual')->willReturn($isVirtual); + $this->orderItemMock->method('getOrder')->willReturn($this->orderMock); $itemAmountCurrencyMock = $this->createMock(AdyenAmountCurrency::class); $itemAmountCurrencyMock->method('getAmountIncludingTax')->willReturn(45); @@ -248,7 +330,8 @@ public function getUrl() $this->cartRepositoryMock, $this->chargedCurrencyMock, $this->configHelperMock, - $this->imageHelperMock + $this->imageHelperMock, + $this->adyenLoggerMock ); $expectedResult = [ @@ -256,10 +339,12 @@ public function getUrl() [ 'id' => '14', 'amountIncludingTax' => 4500, + 'amountExcludingTax' => 4500, 'taxAmount' => 0, + 'taxPercentage' => 0, 'description' => 'Push It Messenger Bag', + 'sku' => '24-WB04', 'quantity' => 1, - 'taxPercentage' => 0, 'productUrl' => 'https://localhost.store/index.php/push-it-messenger-bag.html', 'imageUrl' => '' ], @@ -277,4 +362,15 @@ public function getUrl() $this->assertEquals($expectedResult, $result); } + + /** + * @return array + */ + private static function isVirtualDataProvider(): array + { + return [ + ['isVirtual' => true], + ['isVirtual' => false] + ]; + } } diff --git a/Test/Unit/Helper/PaymentMethodsTest.php b/Test/Unit/Helper/PaymentMethodsTest.php index 17ebcfb1c6..f3260d0fa1 100644 --- a/Test/Unit/Helper/PaymentMethodsTest.php +++ b/Test/Unit/Helper/PaymentMethodsTest.php @@ -1328,12 +1328,49 @@ public function testRemovePaymentMethodsActivation() public function testIsOpenInvoice() { - $paymentMethodInstaceMock = $this->createMock(MethodInterface::class); - $paymentMethodInstaceMock->method('getConfigData') + $paymentMethodInstanceMock = $this->createMock(MethodInterface::class); + $paymentMethodInstanceMock->method('getConfigData') ->with(PaymentMethods::CONFIG_FIELD_IS_OPEN_INVOICE) ->willReturn(true); - $result = $this->paymentMethodsHelper->isOpenInvoice($paymentMethodInstaceMock); + $result = $this->paymentMethodsHelper->isOpenInvoice($paymentMethodInstanceMock); $this->assertTrue($result); } + + /** + * @return array + */ + private static function getRequiresLineItemsDataProvider(): array + { + return [ + ['isOpenInvoice' => true, 'isRequiresOpenInvoiceConfigSet' => false, 'requiresLineItems' => true], + ['isOpenInvoice' => false, 'isRequiresOpenInvoiceConfigSet' => true, 'requiresLineItems' => true], + ['isOpenInvoice' => false, 'isRequiresOpenInvoiceConfigSet' => false, 'requiresLineItems' => false], + ['isOpenInvoice' => true, 'isRequiresOpenInvoiceConfigSet' => true, 'requiresLineItems' => true] + ]; + } + + /** + * @dataProvider getRequiresLineItemsDataProvider() + * + * @param bool $isOpenInvoice + * @param bool $isRequiresOpenInvoiceConfigSet + * @param bool $requiresLineItems + * @return void + */ + public function testGetRequiresLineItems( + bool $isOpenInvoice, + bool $isRequiresOpenInvoiceConfigSet, + bool $requiresLineItems + ) { + $paymentMethodInstanceMock = $this->createMock(MethodInterface::class); + $paymentMethodInstanceMock->method('getConfigData') + ->willReturnMap([ + [PaymentMethods::CONFIG_FIELD_IS_OPEN_INVOICE, null, $isOpenInvoice], + [PaymentMethods::CONFIG_FIELD_REQUIRES_LINE_ITEMS, null, $isRequiresOpenInvoiceConfigSet] + ]); + + $result = $this->paymentMethodsHelper->getRequiresLineItems($paymentMethodInstanceMock); + $this->assertEquals($requiresLineItems, $result); + } } diff --git a/etc/config.xml b/etc/config.xml index 1a5ee9f5ad..9e13c38569 100755 --- a/etc/config.xml +++ b/etc/config.xml @@ -206,6 +206,7 @@ 1 1 0 + 1 adyen-alternative-payment-method diff --git a/etc/di.xml b/etc/di.xml index b054d68ede..7f7dc23295 100755 --- a/etc/di.xml +++ b/etc/di.xml @@ -1117,6 +1117,7 @@ Adyen\Payment\Gateway\Request\CheckoutDataBuilder Adyen\Payment\Gateway\Request\Header\HeaderDataBuilder Adyen\Payment\Gateway\Request\CompanyDataBuilder + Adyen\Payment\Gateway\Request\LineItemsDataBuilder @@ -1160,6 +1161,7 @@ Adyen\Payment\Gateway\Request\Header\HeaderDataBuilder Adyen\Payment\Gateway\Request\GiftcardDataBuilder Adyen\Payment\Gateway\Request\CompanyDataBuilder + Adyen\Payment\Gateway\Request\LineItemsDataBuilder @@ -1212,6 +1214,7 @@ Adyen\Payment\Gateway\Request\OriginDataBuilder Adyen\Payment\Gateway\Request\GiftcardDataBuilder Adyen\Payment\Gateway\Request\CompanyDataBuilder + Adyen\Payment\Gateway\Request\LineItemsDataBuilder