From 26938e0a32b42c7d2cd89533592cc576940a4d53 Mon Sep 17 00:00:00 2001 From: Rafal Janicki Date: Wed, 14 Sep 2022 18:09:50 +0100 Subject: [PATCH 1/6] LYNX-4: Add is_deferred field to AvailablePaymentMethod type to obtain information if payment method is online/offline --- .../Structure/Element/Dependency/Field.php | 8 +- .../Magento/Payment/Model/Method/Free.php | 13 ++- .../Model/Resolver/IsDeferred.php | 84 +++++++++++++++++++ .../PaymentGraphQl/etc/schema.graphqls | 4 + .../Resolver/AvailablePaymentMethods.php | 18 ++-- .../GetIsDeferredPaymentMethodTest.php | 75 +++++++++++++++++ ...PaypalExpressDeferredPaymentMethodTest.php | 76 +++++++++++++++++ 7 files changed, 263 insertions(+), 15 deletions(-) create mode 100644 app/code/Magento/PaymentGraphQl/Model/Resolver/IsDeferred.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/PaymentGraphQl/GetIsDeferredPaymentMethodTest.php create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/PaypalGraphQl/GetIsPaypalExpressDeferredPaymentMethodTest.php diff --git a/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php b/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php index 5214529bf2f0..22705dff72de 100644 --- a/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php +++ b/app/code/Magento/Config/Model/Config/Structure/Element/Dependency/Field.php @@ -5,15 +5,13 @@ */ namespace Magento\Config\Model\Config\Structure\Element\Dependency; -/** - * @api - * @since 100.0.2 - */ - /** * Class Field * * Fields are used to describe possible values for a type/interface. + * + * @api + * @since 100.0.2 */ class Field { diff --git a/app/code/Magento/Payment/Model/Method/Free.php b/app/code/Magento/Payment/Model/Method/Free.php index a02fa4b18d5e..56b96f30aba2 100644 --- a/app/code/Magento/Payment/Model/Method/Free.php +++ b/app/code/Magento/Payment/Model/Method/Free.php @@ -20,16 +20,16 @@ */ class Free extends \Magento\Payment\Model\Method\AbstractMethod { - const PAYMENT_METHOD_FREE_CODE = 'free'; + public const PAYMENT_METHOD_FREE_CODE = 'free'; /** * XML Paths for configuration constants */ - const XML_PATH_PAYMENT_FREE_ACTIVE = 'payment/free/active'; + public const XML_PATH_PAYMENT_FREE_ACTIVE = 'payment/free/active'; - const XML_PATH_PAYMENT_FREE_ORDER_STATUS = 'payment/free/order_status'; + public const XML_PATH_PAYMENT_FREE_ORDER_STATUS = 'payment/free/order_status'; - const XML_PATH_PAYMENT_FREE_PAYMENT_ACTION = 'payment/free/payment_action'; + public const XML_PATH_PAYMENT_FREE_PAYMENT_ACTION = 'payment/free/payment_action'; /** * Payment Method features @@ -50,6 +50,11 @@ class Free extends \Magento\Payment\Model\Method\AbstractMethod */ protected $priceCurrency; + /** + * @var bool + */ + protected $_isOffline = true; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry diff --git a/app/code/Magento/PaymentGraphQl/Model/Resolver/IsDeferred.php b/app/code/Magento/PaymentGraphQl/Model/Resolver/IsDeferred.php new file mode 100644 index 000000000000..77fb250b0e48 --- /dev/null +++ b/app/code/Magento/PaymentGraphQl/Model/Resolver/IsDeferred.php @@ -0,0 +1,84 @@ +paymentData = $paymentData; + $this->overrides = $overrides; + } + + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + if (!$value['code']) { + throw new LocalizedException(__('"code" value should be specified')); + } + return $this->isDeferredPaymentMethod($value['code']); + } + + /** + * Identifies whether the payment method is deferred + * + * @param string $code + * + * @return bool + */ + private function isDeferredPaymentMethod(string $code): bool + { + if (isset($this->overrides['deferred']) && + is_array($this->overrides['deferred']) && + in_array($code, $this->overrides['deferred']) + ) { + return true; + } + if (isset($this->overrides['undeferred']) && + is_array($this->overrides['undeferred']) && + in_array($code, $this->overrides['undeferred']) + ) { + return false; + } + return !$this->paymentData->getMethodInstance($code)->isOffline(); + } +} diff --git a/app/code/Magento/PaymentGraphQl/etc/schema.graphqls b/app/code/Magento/PaymentGraphQl/etc/schema.graphqls index 91150362dc5e..c705ebb8b030 100644 --- a/app/code/Magento/PaymentGraphQl/etc/schema.graphqls +++ b/app/code/Magento/PaymentGraphQl/etc/schema.graphqls @@ -19,3 +19,7 @@ type StoreConfig { check_money_order_max_order_total: String @doc(description: "The maximum order amount required to qualify for the Check/Money Order payment method.") check_money_order_sort_order: Int @doc(description: "A number indicating the position of the Check/Money Order payment method in the list of available payment methods during checkout.") } + +type AvailablePaymentMethod @doc(description: "Describes a payment method that the shopper can use to pay for the order.") { + is_deferred: Boolean! @doc(description: "If the payment method is an online integration") @resolver(class: "\\Magento\\PaymentGraphQl\\Model\\Resolver\\IsDeferred") +} \ No newline at end of file diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/AvailablePaymentMethods.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/AvailablePaymentMethods.php index 0db0d6fcacf8..f09339a3dcda 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/AvailablePaymentMethods.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/AvailablePaymentMethods.php @@ -50,8 +50,13 @@ public function __construct( /** * @inheritdoc */ - public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) - { + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { if (!isset($value['model'])) { throw new LocalizedException(__('"model" value should be specified')); } @@ -79,18 +84,19 @@ private function getPaymentMethodsData(CartInterface $cart): array /** * Checking payment method and shipping method for zero price product */ - if ((int)$grandTotal === 0 && $carrierCode === self::FREE_SHIPPING_METHOD - && $paymentMethod->getCode() === self::FREE_PAYMENT_METHOD_CODE) { + if ((int)$grandTotal === 0 && $carrierCode === self::FREE_SHIPPING_METHOD && + $paymentMethod->getCode() === self::FREE_PAYMENT_METHOD_CODE + ) { return [ [ 'title' => $paymentMethod->getTitle(), - 'code' => $paymentMethod->getCode(), + 'code' => $paymentMethod->getCode() ] ]; } elseif ((int)$grandTotal >= 0) { $paymentMethodsData[] = [ 'title' => $paymentMethod->getTitle(), - 'code' => $paymentMethod->getCode(), + 'code' => $paymentMethod->getCode() ]; } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/PaymentGraphQl/GetIsDeferredPaymentMethodTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/PaymentGraphQl/GetIsDeferredPaymentMethodTest.php new file mode 100644 index 000000000000..8fc9bf89d586 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/PaymentGraphQl/GetIsDeferredPaymentMethodTest.php @@ -0,0 +1,75 @@ +getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); + } + + /** + * @param string $maskedQuoteId + * @return string + */ + private function getQuery(string $maskedQuoteId): string + { + return <<getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId); + $response = $this->graphQlQuery($query); + + self::assertArrayHasKey('cart', $response); + self::assertArrayHasKey('available_payment_methods', $response['cart']); + + self::assertEquals('checkmo', $response['cart']['available_payment_methods'][0]['code']); + self::assertEquals('Check / Money order', $response['cart']['available_payment_methods'][0]['title']); + self::assertEquals(false, $response['cart']['available_payment_methods'][0]['is_deferred']); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/PaypalGraphQl/GetIsPaypalExpressDeferredPaymentMethodTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/PaypalGraphQl/GetIsPaypalExpressDeferredPaymentMethodTest.php new file mode 100644 index 000000000000..8130754ae16b --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/PaypalGraphQl/GetIsPaypalExpressDeferredPaymentMethodTest.php @@ -0,0 +1,76 @@ +getMaskedQuoteIdByReservedOrderId = $objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); + } + + /** + * @param string $maskedQuoteId + * @return string + */ + private function getQuery(string $maskedQuoteId): string + { + return <<getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query = $this->getQuery($maskedQuoteId); + $response = $this->graphQlQuery($query); + + self::assertArrayHasKey('cart', $response); + self::assertArrayHasKey('available_payment_methods', $response['cart']); + self::assertEquals('paypal_express', $response['cart']['available_payment_methods'][0]['code']); + self::assertEquals('PayPal Express Checkout', $response['cart']['available_payment_methods'][0]['title']); + self::assertEquals(true, $response['cart']['available_payment_methods'][0]['is_deferred']); + } +} From 4a59bdd6ba3eb7705b9494119647e2fe1a15a469 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko Date: Fri, 30 Sep 2022 13:33:29 +0100 Subject: [PATCH 2/6] LYNX-45: Introducing purchase order rule data fixtures --- .../Magento/Customer/Test/Fixture/Customer.php | 2 +- .../TestFramework/Fixture/Api/DataMerger.php | 14 +++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Customer/Test/Fixture/Customer.php b/app/code/Magento/Customer/Test/Fixture/Customer.php index 7cd690773174..5b98c94cbe7e 100644 --- a/app/code/Magento/Customer/Test/Fixture/Customer.php +++ b/app/code/Magento/Customer/Test/Fixture/Customer.php @@ -28,7 +28,7 @@ class Customer implements RevertibleDataFixtureInterface { private const DEFAULT_DATA = [ - 'password' => null, + 'password' => 'password', CustomerInterface::ID => null, CustomerInterface::CONFIRMATION => null, CustomerInterface::CREATED_AT => null, diff --git a/dev/tests/integration/framework/Magento/TestFramework/Fixture/Api/DataMerger.php b/dev/tests/integration/framework/Magento/TestFramework/Fixture/Api/DataMerger.php index e06dc3b5bbd4..7c076452ea7f 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Fixture/Api/DataMerger.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Fixture/Api/DataMerger.php @@ -27,15 +27,11 @@ class DataMerger public function merge(array $defaultData, array $data, bool $isExtensible = true): array { if ($isExtensible) { - if (!isset($data[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY])) { - $data[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY] = []; + if (isset($data[CustomAttributesDataInterface::CUSTOM_ATTRIBUTES])) { + $data[CustomAttributesDataInterface::CUSTOM_ATTRIBUTES] = $this->convertCustomAttributesToMap( + $data[CustomAttributesDataInterface::CUSTOM_ATTRIBUTES] + ); } - if (!isset($data[CustomAttributesDataInterface::CUSTOM_ATTRIBUTES])) { - $data[CustomAttributesDataInterface::CUSTOM_ATTRIBUTES] = []; - } - $data[CustomAttributesDataInterface::CUSTOM_ATTRIBUTES] = $this->convertCustomAttributesToMap( - $data[CustomAttributesDataInterface::CUSTOM_ATTRIBUTES] - ); foreach ($data as $key => $value) { if (!array_key_exists($key, $defaultData)) { if (array_key_exists($key, $defaultData[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY])) { @@ -50,7 +46,7 @@ public function merge(array $defaultData, array $data, bool $isExtensible = true $result = $this->mergeRecursive($defaultData, $data); - if ($isExtensible) { + if ($isExtensible && isset($result[CustomAttributesDataInterface::CUSTOM_ATTRIBUTES])) { $result[CustomAttributesDataInterface::CUSTOM_ATTRIBUTES] = $this->convertCustomAttributesToCollection( $result[CustomAttributesDataInterface::CUSTOM_ATTRIBUTES] ); From f1996bb8042a8fed857b845fe3cd3b6ec4f23db5 Mon Sep 17 00:00:00 2001 From: Rafal Janicki Date: Wed, 5 Oct 2022 10:22:42 +0100 Subject: [PATCH 3/6] LYNX-47: Update magento-coding-standard to version 26 --- composer.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/composer.lock b/composer.lock index cd6c332c5701..1ce6f6019fa3 100644 --- a/composer.lock +++ b/composer.lock @@ -10399,16 +10399,16 @@ }, { "name": "magento/magento-coding-standard", - "version": "23", + "version": "26", "source": { "type": "git", "url": "https://github.com/magento/magento-coding-standard.git", - "reference": "dba4d2b4ba78d3dbe3e4d7100bda6fce4aaacbf9" + "reference": "0263b8952b509848ffdab1ea9ab738f48450677c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento-coding-standard/zipball/dba4d2b4ba78d3dbe3e4d7100bda6fce4aaacbf9", - "reference": "dba4d2b4ba78d3dbe3e4d7100bda6fce4aaacbf9", + "url": "https://api.github.com/repos/magento/magento-coding-standard/zipball/0263b8952b509848ffdab1ea9ab738f48450677c", + "reference": "0263b8952b509848ffdab1ea9ab738f48450677c", "shasum": "" }, "require": { @@ -10441,9 +10441,9 @@ "description": "A set of Magento specific PHP CodeSniffer rules.", "support": { "issues": "https://github.com/magento/magento-coding-standard/issues", - "source": "https://github.com/magento/magento-coding-standard/tree/v23" + "source": "https://github.com/magento/magento-coding-standard/tree/v26" }, - "time": "2022-06-01T09:03:27+00:00" + "time": "2022-10-04T10:45:15+00:00" }, { "name": "magento/magento2-functional-testing-framework", @@ -13573,5 +13573,5 @@ "lib-libxml": "*" }, "platform-dev": [], - "plugin-api-version": "2.2.0" + "plugin-api-version": "2.3.0" } From 29389e14338bc9cbfd1be080e7e9f4b89df6291a Mon Sep 17 00:00:00 2001 From: Rafal Janicki Date: Thu, 27 Oct 2022 13:49:11 +0100 Subject: [PATCH 4/6] LYNX-49: Replaced usage of undefined array_first function with reset function - array_first function is defined in weew/helpers-array package that is not direct dependency --- .../Api/SearchCriteria/CollectionProcessor/FilterProcessor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php b/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php index 2f4c097b1f02..9668f4e2239d 100644 --- a/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php +++ b/lib/internal/Magento/Framework/Api/SearchCriteria/CollectionProcessor/FilterProcessor.php @@ -140,7 +140,7 @@ private function checkFromTo(&$fields, &$conditions) $_fields = array_unique($fields); $_conditions = []; foreach ($conditions as $condition) { - $_conditions[array_key_first($condition)] = array_first($condition); + $_conditions[array_key_first($condition)] = reset($condition); } if ((count($_fields) == 1) && (count($_conditions) == 2) && isset($_conditions['from']) && isset($_conditions['to']) From 02b6903e88b7eaeeb290ee4a3255ae5c9838dabe Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko Date: Fri, 28 Oct 2022 12:08:07 +0100 Subject: [PATCH 5/6] LYNX-51: Added ability to extend definition of active cart --- .../Model/Cart/GetCartForCheckout.php | 73 +++++++++++++ .../Model/Cart/GetCartForUser.php | 100 +++--------------- .../QuoteGraphQl/Model/Cart/IsActive.php | 27 +++++ .../Model/Cart/UpdateCartCurrency.php | 74 +++++++++++++ .../Model/Resolver/PlaceOrder.php | 14 +-- .../Resolver/SetPaymentAndPlaceOrder.php | 14 +-- 6 files changed, 201 insertions(+), 101 deletions(-) create mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForCheckout.php create mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/IsActive.php create mode 100644 app/code/Magento/QuoteGraphQl/Model/Cart/UpdateCartCurrency.php diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForCheckout.php b/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForCheckout.php new file mode 100644 index 000000000000..4b2d1afdea00 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForCheckout.php @@ -0,0 +1,73 @@ +checkoutAllowance = $checkoutAllowance; + $this->getCartForUser = $getCartForUser; + } + + /** + * Gets the cart for the user validated and configured for guest checkout if applicable + * + * @param string $cartHash + * @param int|null $customerId + * @param int $storeId + * @return Quote + * @throws GraphQlAuthorizationException + * @throws GraphQlInputException + * @throws GraphQlNoSuchEntityException + */ + public function execute(string $cartHash, ?int $customerId, int $storeId): Quote + { + try { + $cart = $this->getCartForUser->execute($cartHash, $customerId, $storeId); + } catch (NoSuchEntityException $e) { + throw new GraphQlNoSuchEntityException(__($e->getMessage()), $e); + } + $this->checkoutAllowance->execute($cart); + + if (null === $customerId || 0 === $customerId) { + if (!$cart->getCustomerEmail()) { + throw new GraphQlInputException(__("Guest email for cart is missing.")); + } + $cart->setCheckoutMethod(CartManagementInterface::METHOD_GUEST); + } + + return $cart; + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php b/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php index 21df9465a08e..77a31cc3cd02 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/GetCartForUser.php @@ -7,16 +7,13 @@ namespace Magento\QuoteGraphQl\Model\Cart; -use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; -use Magento\Quote\Api\CartManagementInterface; use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface; use Magento\Quote\Model\Quote; -use Magento\Store\Api\StoreRepositoryInterface; /** * Get cart @@ -34,31 +31,31 @@ class GetCartForUser private $cartRepository; /** - * @var CheckCartCheckoutAllowance + * @var IsActive */ - private $checkoutAllowance; + private $isActive; /** - * @var StoreRepositoryInterface + * @var UpdateCartCurrency */ - private $storeRepository; + private $updateCartCurrency; /** * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId * @param CartRepositoryInterface $cartRepository - * @param CheckCartCheckoutAllowance $checkoutAllowance - * @param StoreRepositoryInterface $storeRepository + * @param IsActive $isActive + * @param UpdateCartCurrency $updateCartCurrency */ public function __construct( MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId, CartRepositoryInterface $cartRepository, - CheckCartCheckoutAllowance $checkoutAllowance, - StoreRepositoryInterface $storeRepository = null + IsActive $isActive, + UpdateCartCurrency $updateCartCurrency ) { $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; $this->cartRepository = $cartRepository; - $this->checkoutAllowance = $checkoutAllowance; - $this->storeRepository = $storeRepository ?: ObjectManager::getInstance()->get(StoreRepositoryInterface::class); + $this->isActive = $isActive; + $this->updateCartCurrency = $updateCartCurrency; } /** @@ -77,26 +74,19 @@ public function execute(string $cartHash, ?int $customerId, int $storeId): Quote { try { $cartId = $this->maskedQuoteIdToQuoteId->execute($cartHash); - } catch (NoSuchEntityException $exception) { - throw new GraphQlNoSuchEntityException( - __('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $cartHash]) - ); - } - - try { /** @var Quote $cart */ $cart = $this->cartRepository->get($cartId); - } catch (NoSuchEntityException $e) { + } catch (NoSuchEntityException $exception) { throw new GraphQlNoSuchEntityException( __('Could not find a cart with ID "%masked_cart_id"', ['masked_cart_id' => $cartHash]) ); } - if (false === (bool)$cart->getIsActive()) { + if (false === (bool)$this->isActive->execute($cart)) { throw new GraphQlNoSuchEntityException(__('The cart isn\'t active.')); } - $cart = $this->updateCartCurrency($cart, $storeId); + $cart = $this->updateCartCurrency->execute($cart, $storeId); $cartCustomerId = (int)$cart->getCustomerId(); @@ -115,68 +105,4 @@ public function execute(string $cartHash, ?int $customerId, int $storeId): Quote } return $cart; } - - /** - * Gets the cart for the user validated and configured for guest checkout if applicable - * - * @param string $cartHash - * @param int|null $customerId - * @param int $storeId - * @return Quote - * @throws GraphQlAuthorizationException - * @throws GraphQlInputException - * @throws GraphQlNoSuchEntityException - */ - public function getCartForCheckout(string $cartHash, ?int $customerId, int $storeId): Quote - { - try { - $cart = $this->execute($cartHash, $customerId, $storeId); - } catch (NoSuchEntityException $e) { - throw new GraphQlNoSuchEntityException(__($e->getMessage()), $e); - } - $this->checkoutAllowance->execute($cart); - - if ((null === $customerId || 0 === $customerId)) { - if (!$cart->getCustomerEmail()) { - throw new GraphQlInputException(__("Guest email for cart is missing.")); - } - $cart->setCheckoutMethod(CartManagementInterface::METHOD_GUEST); - } - - return $cart; - } - - /** - * Sets cart currency based on specified store. - * - * @param Quote $cart - * @param int $storeId - * @return Quote - * @throws GraphQlInputException - * @throws NoSuchEntityException - */ - private function updateCartCurrency(Quote $cart, int $storeId): Quote - { - $cartStore = $this->storeRepository->getById($cart->getStoreId()); - $currentCartCurrencyCode = $cartStore->getCurrentCurrency()->getCode(); - if ((int)$cart->getStoreId() !== $storeId) { - $newStore = $this->storeRepository->getById($storeId); - if ($cartStore->getWebsite() !== $newStore->getWebsite()) { - throw new GraphQlInputException( - __('Can\'t assign cart to store in different website.') - ); - } - $cart->setStoreId($storeId); - $cart->setStoreCurrencyCode($newStore->getCurrentCurrency()); - $cart->setQuoteCurrencyCode($newStore->getCurrentCurrency()); - } elseif ($cart->getQuoteCurrencyCode() !== $currentCartCurrencyCode) { - $cart->setQuoteCurrencyCode($cartStore->getCurrentCurrency()); - } else { - return $cart; - } - $this->cartRepository->save($cart); - $cart = $this->cartRepository->get($cart->getId()); - - return $cart; - } } diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/IsActive.php b/app/code/Magento/QuoteGraphQl/Model/Cart/IsActive.php new file mode 100644 index 000000000000..531d7ba11921 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/IsActive.php @@ -0,0 +1,27 @@ +getIsActive(); + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/UpdateCartCurrency.php b/app/code/Magento/QuoteGraphQl/Model/Cart/UpdateCartCurrency.php new file mode 100644 index 000000000000..109ee7b4ef54 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/UpdateCartCurrency.php @@ -0,0 +1,74 @@ +cartRepository = $cartRepository; + $this->storeRepository = $storeRepository; + } + + /** + * Sets cart currency based on specified store. + * + * @param Quote $cart + * @param int $storeId + * @return Quote + * @throws GraphQlInputException|NoSuchEntityException|LocalizedException + */ + public function execute(Quote $cart, int $storeId): Quote + { + $cartStore = $this->storeRepository->getById($cart->getStoreId()); + $currentCartCurrencyCode = $cartStore->getCurrentCurrency()->getCode(); + if ((int)$cart->getStoreId() !== $storeId) { + $newStore = $this->storeRepository->getById($storeId); + if ($cartStore->getWebsite() !== $newStore->getWebsite()) { + throw new GraphQlInputException( + __('Can\'t assign cart to store in different website.') + ); + } + $cart->setStoreId($storeId); + $cart->setStoreCurrencyCode($newStore->getCurrentCurrency()); + $cart->setQuoteCurrencyCode($newStore->getCurrentCurrency()); + } elseif ($cart->getQuoteCurrencyCode() !== $currentCartCurrencyCode) { + $cart->setQuoteCurrencyCode($cartStore->getCurrentCurrency()); + } else { + return $cart; + } + $this->cartRepository->save($cart); + return $this->cartRepository->get($cart->getId()); + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/PlaceOrder.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/PlaceOrder.php index f338732e4079..1539612cc265 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/PlaceOrder.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/PlaceOrder.php @@ -13,7 +13,7 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\GraphQl\Helper\Error\AggregateExceptionMessageFormatter; -use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; +use Magento\QuoteGraphQl\Model\Cart\GetCartForCheckout; use Magento\QuoteGraphQl\Model\Cart\PlaceOrder as PlaceOrderModel; use Magento\Sales\Api\OrderRepositoryInterface; @@ -23,9 +23,9 @@ class PlaceOrder implements ResolverInterface { /** - * @var GetCartForUser + * @var GetCartForCheckout */ - private $getCartForUser; + private $getCartForCheckout; /** * @var PlaceOrderModel @@ -43,18 +43,18 @@ class PlaceOrder implements ResolverInterface private $errorMessageFormatter; /** - * @param GetCartForUser $getCartForUser + * @param GetCartForCheckout $getCartForCheckout * @param PlaceOrderModel $placeOrder * @param OrderRepositoryInterface $orderRepository * @param AggregateExceptionMessageFormatter $errorMessageFormatter */ public function __construct( - GetCartForUser $getCartForUser, + GetCartForCheckout $getCartForCheckout, PlaceOrderModel $placeOrder, OrderRepositoryInterface $orderRepository, AggregateExceptionMessageFormatter $errorMessageFormatter ) { - $this->getCartForUser = $getCartForUser; + $this->getCartForCheckout = $getCartForCheckout; $this->placeOrder = $placeOrder; $this->orderRepository = $orderRepository; $this->errorMessageFormatter = $errorMessageFormatter; @@ -73,7 +73,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $storeId = (int)$context->getExtensionAttributes()->getStore()->getId(); try { - $cart = $this->getCartForUser->getCartForCheckout($maskedCartId, $userId, $storeId); + $cart = $this->getCartForCheckout->execute($maskedCartId, $userId, $storeId); $orderId = $this->placeOrder->execute($cart, $maskedCartId, $userId); $order = $this->orderRepository->get($orderId); } catch (LocalizedException $e) { diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentAndPlaceOrder.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentAndPlaceOrder.php index 500c2aa35999..1b9ee4ad3907 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentAndPlaceOrder.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentAndPlaceOrder.php @@ -15,7 +15,7 @@ use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; -use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; +use Magento\QuoteGraphQl\Model\Cart\GetCartForCheckout; use Magento\QuoteGraphQl\Model\Cart\SetPaymentAndPlaceOrder as SetPaymentAndPlaceOrderModel; use Magento\Sales\Api\OrderRepositoryInterface; @@ -29,9 +29,9 @@ class SetPaymentAndPlaceOrder implements ResolverInterface { /** - * @var GetCartForUser + * @var GetCartForCheckout */ - private $getCartForUser; + private $getCartForCheckout; /** * @var SetPaymentAndPlaceOrderModel @@ -44,16 +44,16 @@ class SetPaymentAndPlaceOrder implements ResolverInterface private $orderRepository; /** - * @param GetCartForUser $getCartForUser + * @param GetCartForCheckout $getCartForCheckout * @param SetPaymentAndPlaceOrderModel $setPaymentAndPlaceOrder * @param OrderRepositoryInterface $orderRepository */ public function __construct( - GetCartForUser $getCartForUser, + GetCartForCheckout $getCartForCheckout, SetPaymentAndPlaceOrderModel $setPaymentAndPlaceOrder, OrderRepositoryInterface $orderRepository ) { - $this->getCartForUser = $getCartForUser; + $this->getCartForCheckout = $getCartForCheckout; $this->setPaymentAndPlaceOrder = $setPaymentAndPlaceOrder; $this->orderRepository = $orderRepository; } @@ -78,7 +78,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $storeId = (int)$context->getExtensionAttributes()->getStore()->getId(); try { - $cart = $this->getCartForUser->getCartForCheckout($maskedCartId, $userId, $storeId); + $cart = $this->getCartForCheckout->execute($maskedCartId, $userId, $storeId); $orderId = $this->setPaymentAndPlaceOrder->execute($cart, $maskedCartId, $userId, $paymentData); $order = $this->orderRepository->get($orderId); } catch (GraphQlInputException | GraphQlNoSuchEntityException | GraphQlAuthorizationException $e) { From 110607d0ba8e9df88218017a21edeb8650e225e2 Mon Sep 17 00:00:00 2001 From: Sergii Ivashchenko Date: Wed, 16 Nov 2022 15:45:42 +0000 Subject: [PATCH 6/6] LYNX-15: Extracted add product to cart error creation --- .../Quote/Model/Cart/AddProductsToCart.php | 89 ++++--------------- .../Model/Cart/AddProductsToCartError.php | 71 +++++++++++++++ 2 files changed, 87 insertions(+), 73 deletions(-) create mode 100644 app/code/Magento/Quote/Model/Cart/AddProductsToCartError.php diff --git a/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php b/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php index 2086646d855f..9be1e9d32e37 100644 --- a/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php +++ b/app/code/Magento/Quote/Model/Cart/AddProductsToCart.php @@ -7,8 +7,6 @@ namespace Magento\Quote\Model\Cart; -use Magento\Catalog\Api\ProductRepositoryInterface; -use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Model\Cart\BuyRequest\BuyRequestBuilder; @@ -23,29 +21,6 @@ */ class AddProductsToCart { - /**#@+ - * Error message codes - */ - private const ERROR_PRODUCT_NOT_FOUND = 'PRODUCT_NOT_FOUND'; - private const ERROR_INSUFFICIENT_STOCK = 'INSUFFICIENT_STOCK'; - private const ERROR_NOT_SALABLE = 'NOT_SALABLE'; - private const ERROR_UNDEFINED = 'UNDEFINED'; - /**#@-*/ - - /** - * List of error messages and codes. - */ - private const MESSAGE_CODES = [ - 'Could not find a product with SKU' => self::ERROR_PRODUCT_NOT_FOUND, - 'The required options you selected are not available' => self::ERROR_NOT_SALABLE, - 'Product that you are trying to add is not available.' => self::ERROR_NOT_SALABLE, - 'This product is out of stock' => self::ERROR_INSUFFICIENT_STOCK, - 'There are no source items' => self::ERROR_NOT_SALABLE, - 'The fewest you may purchase is' => self::ERROR_INSUFFICIENT_STOCK, - 'The most you may purchase is' => self::ERROR_INSUFFICIENT_STOCK, - 'The requested qty is not available' => self::ERROR_INSUFFICIENT_STOCK, - ]; - /** * @var CartRepositoryInterface */ @@ -67,25 +42,29 @@ class AddProductsToCart private $productReader; /** - * @param ProductRepositoryInterface $productRepository + * @var AddProductsToCartError + */ + private $error; + + /** * @param CartRepositoryInterface $cartRepository * @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId * @param BuyRequestBuilder $requestBuilder - * @param ProductReaderInterface|null $productReader - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @param ProductReaderInterface $productReader + * @param AddProductsToCartError $addProductsToCartError */ public function __construct( - ProductRepositoryInterface $productRepository, CartRepositoryInterface $cartRepository, MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId, BuyRequestBuilder $requestBuilder, - ProductReaderInterface $productReader = null + ProductReaderInterface $productReader, + AddProductsToCartError $addProductsToCartError ) { $this->cartRepository = $cartRepository; $this->maskedQuoteIdToQuoteId = $maskedQuoteIdToQuoteId; $this->requestBuilder = $requestBuilder; - $this->productReader = $productReader ?: ObjectManager::getInstance()->get(ProductReaderInterface::class); + $this->productReader = $productReader; + $this->error = $addProductsToCartError; } /** @@ -106,7 +85,7 @@ public function execute(string $maskedCartId, array $cartItems): AddProductsToCa /** @var MessageInterface $error */ foreach ($errors as $error) { - $allErrors[] = $this->createError($error->getText()); + $allErrors[] = $this->error->create($error->getText()); } } @@ -181,14 +160,14 @@ private function addItemToCart(Quote $cart, Data\CartItem $cartItem, int $cartIt $result = null; if ($cartItem->getQuantity() <= 0) { - $errors[] = $this->createError( + $errors[] = $this->error->create( __('The product quantity should be greater than 0')->render(), $cartItemPosition ); } else { $product = $this->productReader->getProductBySku($sku); if (!$product || !$product->isSaleable() || !$product->isAvailable()) { - $errors[] = $this->createError( + $errors[] = $this->error->create( __('Could not find a product with SKU "%sku"', ['sku' => $sku])->render(), $cartItemPosition ); @@ -196,7 +175,7 @@ private function addItemToCart(Quote $cart, Data\CartItem $cartItem, int $cartIt try { $result = $cart->addProduct($product, $this->requestBuilder->build($cartItem)); } catch (\Throwable $e) { - $errors[] = $this->createError( + $errors[] = $this->error->create( __($e->getMessage())->render(), $cartItemPosition ); @@ -205,7 +184,7 @@ private function addItemToCart(Quote $cart, Data\CartItem $cartItem, int $cartIt if (is_string($result)) { foreach (array_unique(explode("\n", $result)) as $error) { - $errors[] = $this->createError(__($error)->render(), $cartItemPosition); + $errors[] = $this->error->create(__($error)->render(), $cartItemPosition); } } } @@ -213,42 +192,6 @@ private function addItemToCart(Quote $cart, Data\CartItem $cartItem, int $cartIt return $errors; } - /** - * Returns an error object - * - * @param string $message - * @param int $cartItemPosition - * @return Data\Error - */ - private function createError(string $message, int $cartItemPosition = 0): Data\Error - { - return new Data\Error( - $message, - $this->getErrorCode($message), - $cartItemPosition - ); - } - - /** - * Get message error code. - * - * TODO: introduce a separate class for getting error code from a message - * - * @param string $message - * @return string - */ - private function getErrorCode(string $message): string - { - foreach (self::MESSAGE_CODES as $codeMessage => $code) { - if (false !== stripos($message, $codeMessage)) { - return $code; - } - } - - /* If no code was matched, return the default one */ - return self::ERROR_UNDEFINED; - } - /** * Creates a new output from existing errors * diff --git a/app/code/Magento/Quote/Model/Cart/AddProductsToCartError.php b/app/code/Magento/Quote/Model/Cart/AddProductsToCartError.php new file mode 100644 index 000000000000..fe8c0d72d465 --- /dev/null +++ b/app/code/Magento/Quote/Model/Cart/AddProductsToCartError.php @@ -0,0 +1,71 @@ + self::ERROR_PRODUCT_NOT_FOUND, + 'The required options you selected are not available' => self::ERROR_NOT_SALABLE, + 'Product that you are trying to add is not available.' => self::ERROR_NOT_SALABLE, + 'This product is out of stock' => self::ERROR_INSUFFICIENT_STOCK, + 'There are no source items' => self::ERROR_NOT_SALABLE, + 'The fewest you may purchase is' => self::ERROR_INSUFFICIENT_STOCK, + 'The most you may purchase is' => self::ERROR_INSUFFICIENT_STOCK, + 'The requested qty is not available' => self::ERROR_INSUFFICIENT_STOCK, + ]; + + /** + * Returns an error object + * + * @param string $message + * @param int $cartItemPosition + * @return Data\Error + */ + public function create(string $message, int $cartItemPosition = 0): Data\Error + { + return new Data\Error( + $message, + $this->getErrorCode($message), + $cartItemPosition + ); + } + + /** + * Get message error code. + * + * @param string $message + * @return string + */ + private function getErrorCode(string $message): string + { + foreach (self::MESSAGE_CODES as $codeMessage => $code) { + if (false !== stripos($message, $codeMessage)) { + return $code; + } + } + + /* If no code was matched, return the default one */ + return self::ERROR_UNDEFINED; + } +}