Skip to content

Commit c9453c9

Browse files
bl4deeliseacornejosumesh-GL
authored andcommitted
LYNX-305: Add estimated cost of shipping methods for guest cart (#176)
* LYNX-305: GraphQL query to obtain estimated cost of shipping methods for cart based on country/address * LYNX-314: Been able to run tests locally when add product to cart fixture * LYNX-305: CR changes --------- Co-authored-by: eliseacornejo <[email protected]> Co-authored-by: Sumesh P <[email protected]>
1 parent a182f92 commit c9453c9

File tree

7 files changed

+888
-29
lines changed

7 files changed

+888
-29
lines changed

app/code/Magento/Catalog/Test/Fixture/Product.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class Product implements RevertibleDataFixtureInterface
5353
'media_gallery_entries' => [],
5454
'tier_prices' => [],
5555
'created_at' => null,
56-
'updated_at' => null,
56+
'updated_at' => null
5757
];
5858

5959
private const DEFAULT_PRODUCT_LINK_DATA = [
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<?php
2+
/**
3+
* Copyright 2023 Adobe
4+
* All Rights Reserved.
5+
*
6+
* NOTICE: All information contained herein is, and remains
7+
* the property of Adobe and its suppliers, if any. The intellectual
8+
* and technical concepts contained herein are proprietary to Adobe
9+
* and its suppliers and are protected by all applicable intellectual
10+
* property laws, including trade secret and copyright laws.
11+
* Dissemination of this information or reproduction of this material
12+
* is strictly forbidden unless prior written permission is obtained from
13+
* Adobe.
14+
*/
15+
declare(strict_types=1);
16+
17+
namespace Magento\OfflineShipping\Test\Fixture;
18+
19+
use Magento\Framework\DataObject;
20+
use Magento\TestFramework\Fixture\Api\ServiceFactory;
21+
use Magento\TestFramework\Fixture\Api\DataMerger;
22+
use Magento\TestFramework\Fixture\RevertibleDataFixtureInterface;
23+
use Magento\TestFramework\Helper\Bootstrap;
24+
use Magento\Framework\ObjectManagerInterface;
25+
use Magento\Framework\App\ResourceConnection;
26+
use Magento\Framework\DB\Adapter\AdapterInterface;
27+
use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate;
28+
29+
class TablerateFixture implements RevertibleDataFixtureInterface
30+
{
31+
private const DEFAULT_DATA = [
32+
'website_id' => 1,
33+
'dest_country_id' => 'US',
34+
'dest_region_id' => 0,
35+
'dest_zip' => '*',
36+
'condition_name' => 'package_qty',
37+
'condition_value' => 1,
38+
'price' => 10,
39+
'cost' => 0
40+
];
41+
42+
/**
43+
* @var AdapterInterface $connection
44+
*/
45+
private AdapterInterface $connection;
46+
47+
/**
48+
* @var ObjectManagerInterface $objectManager
49+
*/
50+
private ObjectManagerInterface $objectManager;
51+
52+
/**
53+
* @param ServiceFactory $serviceFactory
54+
* @param DataMerger $dataMerger
55+
*/
56+
public function __construct(
57+
private ServiceFactory $serviceFactory,
58+
private DataMerger $dataMerger,
59+
) {
60+
$this->objectManager = Bootstrap::getObjectManager();
61+
$this->connection = $this->objectManager->get(ResourceConnection::class)->getConnection();
62+
}
63+
64+
/**
65+
* @inheritDoc
66+
*/
67+
public function apply(array $data = []): ?DataObject
68+
{
69+
$resourceModel = $this->objectManager->create(Tablerate::class);
70+
$data = $this->dataMerger->merge($this::DEFAULT_DATA, $data);
71+
$columns = [
72+
'website_id',
73+
'dest_country_id',
74+
'dest_region_id',
75+
'dest_zip',
76+
'condition_name',
77+
'condition_value',
78+
'price',
79+
'cost'
80+
];
81+
$resourceModel->getConnection()->insertArray(
82+
$resourceModel->getMainTable(),
83+
$columns,
84+
[
85+
$data
86+
]
87+
);
88+
return new DataObject($data);
89+
}
90+
91+
/**
92+
* @inheritDoc
93+
*/
94+
public function revert(DataObject $data): void
95+
{
96+
$resourceModel = $this->objectManager->create(Tablerate::class);
97+
$this->connection->query("DELETE FROM {$resourceModel->getTable('shipping_tablerate')};");
98+
}
99+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
/**
3+
* Copyright 2023 Adobe
4+
* All Rights Reserved.
5+
*
6+
* NOTICE: All information contained herein is, and remains
7+
* the property of Adobe and its suppliers, if any. The intellectual
8+
* and technical concepts contained herein are proprietary to Adobe
9+
* and its suppliers and are protected by all applicable intellectual
10+
* property laws, including trade secret and copyright laws.
11+
* Dissemination of this information or reproduction of this material
12+
* is strictly forbidden unless prior written permission is obtained from
13+
* Adobe.
14+
*/
15+
declare(strict_types=1);
16+
17+
namespace Magento\QuoteGraphQl\Model;
18+
19+
class FormatMoneyTypeData
20+
{
21+
/**
22+
* Converts money data to unified format
23+
*
24+
* @param array $data
25+
* @param string $currencyCode
26+
* @return array
27+
*/
28+
public function execute(array $data, string $currencyCode): array
29+
{
30+
if (isset($data['amount'])) {
31+
$data['amount'] = [
32+
'value' => $data['amount'],
33+
'currency' => $currencyCode
34+
];
35+
}
36+
37+
/** @deprecated The field should not be used on the storefront */
38+
$data['base_amount'] = null;
39+
40+
if (isset($data['price_excl_tax'])) {
41+
$data['price_excl_tax'] = [
42+
'value' => $data['price_excl_tax'],
43+
'currency' => $currencyCode
44+
];
45+
}
46+
47+
if (isset($data['price_incl_tax'])) {
48+
$data['price_incl_tax'] = [
49+
'value' => $data['price_incl_tax'],
50+
'currency' => $currencyCode
51+
];
52+
}
53+
return $data;
54+
}
55+
}
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
<?php
2+
/**
3+
* Copyright 2023 Adobe
4+
* All Rights Reserved.
5+
*
6+
* NOTICE: All information contained herein is, and remains
7+
* the property of Adobe and its suppliers, if any. The intellectual
8+
* and technical concepts contained herein are proprietary to Adobe
9+
* and its suppliers and are protected by all applicable intellectual
10+
* property laws, including trade secret and copyright laws.
11+
* Dissemination of this information or reproduction of this material
12+
* is strictly forbidden unless prior written permission is obtained from
13+
* Adobe.
14+
*/
15+
declare(strict_types=1);
16+
17+
namespace Magento\QuoteGraphQl\Model\Resolver;
18+
19+
use Magento\Framework\Exception\NoSuchEntityException;
20+
use Magento\Framework\Api\ExtensibleDataObjectConverter;
21+
use Magento\Framework\GraphQl\Config\Element\Field;
22+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
23+
use Magento\Framework\GraphQl\Query\ResolverInterface;
24+
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
25+
use Magento\Quote\Api\CartRepositoryInterface;
26+
use Magento\Quote\Api\Data\AddressInterface;
27+
use Magento\Quote\Api\Data\CartInterface;
28+
use Magento\Quote\Api\Data\ShippingMethodInterface;
29+
use Magento\Quote\Api\ShipmentEstimationInterface;
30+
use Magento\Quote\Model\MaskedQuoteIdToQuoteIdInterface;
31+
use Magento\Quote\Model\Quote\AddressFactory;
32+
use Magento\Quote\Model\Cart\ShippingMethodConverter;
33+
use Magento\QuoteGraphQl\Model\FormatMoneyTypeData;
34+
35+
/**
36+
* @inheritdoc
37+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
38+
*/
39+
class EstimateShippingMethods implements ResolverInterface
40+
{
41+
/**
42+
* @param MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId
43+
* @param CartRepositoryInterface $cartRepository
44+
* @param AddressFactory $addressFactory
45+
* @param ShipmentEstimationInterface $shipmentEstimation
46+
* @param ExtensibleDataObjectConverter $dataObjectConverter
47+
* @param ShippingMethodConverter $shippingMethodConverter
48+
* @param FormatMoneyTypeData $formatMoneyTypeData
49+
*/
50+
public function __construct(
51+
private MaskedQuoteIdToQuoteIdInterface $maskedQuoteIdToQuoteId,
52+
private CartRepositoryInterface $cartRepository,
53+
private AddressFactory $addressFactory,
54+
private ShipmentEstimationInterface $shipmentEstimation,
55+
private ExtensibleDataObjectConverter $dataObjectConverter,
56+
private ShippingMethodConverter $shippingMethodConverter,
57+
private FormatMoneyTypeData $formatMoneyTypeData,
58+
) {
59+
}
60+
61+
/**
62+
* @inheritdoc
63+
*/
64+
public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
65+
{
66+
$this->validateInput($args);
67+
try {
68+
$cart = $this->cartRepository->get($this->maskedQuoteIdToQuoteId->execute($args['input']['cart_id']));
69+
} catch (NoSuchEntityException $ex) {
70+
throw new GraphQlInputException(
71+
__(
72+
'Could not find a cart with ID "%masked_id"',
73+
[
74+
'masked_id' => $args['input']['cart_id']
75+
]
76+
)
77+
);
78+
}
79+
return $this->getAvailableShippingMethodsForAddress($args, $cart);
80+
}
81+
82+
/**
83+
* Validates arguments passed to resolver
84+
*
85+
* @param array $args
86+
* @throws GraphQlInputException
87+
*/
88+
private function validateInput(array $args)
89+
{
90+
if (empty($args['input']['cart_id'])) {
91+
throw new GraphQlInputException(__('Required parameter "cart_id" is missing'));
92+
}
93+
94+
if (empty($args['input'][AddressInterface::KEY_COUNTRY_ID])) {
95+
throw new GraphQlInputException(
96+
__(
97+
'Required parameter "%country_id" is missing',
98+
[
99+
'country_id' => AddressInterface::KEY_COUNTRY_ID
100+
]
101+
)
102+
);
103+
}
104+
105+
if (isset($args['input']['address']) && empty($args['input']['address'])) {
106+
throw new GraphQlInputException(__('Parameter(s) for address are missing'));
107+
}
108+
109+
if (isset($args['input']['address']['region']) && empty($args['input']['address']['region'])) {
110+
throw new GraphQlInputException(__('Parameter(s) for region are missing'));
111+
}
112+
}
113+
114+
/**
115+
* Get the list of available shipping methods given a cart, country_id and optional customer address parameters
116+
*
117+
* @param array $args
118+
* @param CartInterface $cart
119+
* @return array
120+
*/
121+
private function getAvailableShippingMethodsForAddress(array $args, CartInterface $cart): array
122+
{
123+
/** @var $address AddressInterface */
124+
$address = $this->addressFactory->create();
125+
$shippingMethods = [];
126+
127+
$address->addData([
128+
AddressInterface::KEY_COUNTRY_ID => $args['input'][AddressInterface::KEY_COUNTRY_ID]
129+
]);
130+
if (!empty($args['input']['address'])) {
131+
$data = $args['input']['address'];
132+
if (!empty($data['region'])) {
133+
$address->addData([
134+
AddressInterface::KEY_REGION => $data['region'][AddressInterface::KEY_REGION] ?? '',
135+
AddressInterface::KEY_REGION_ID => $data['region'][AddressInterface::KEY_REGION_ID] ?? '',
136+
AddressInterface::KEY_REGION_CODE => $data['region'][AddressInterface::KEY_REGION_CODE] ?? ''
137+
]);
138+
}
139+
$address->addData([
140+
AddressInterface::KEY_FIRSTNAME => $data[AddressInterface::KEY_FIRSTNAME] ?? '',
141+
AddressInterface::KEY_LASTNAME => $data[AddressInterface::KEY_LASTNAME] ?? '',
142+
AddressInterface::KEY_MIDDLENAME => $data[AddressInterface::KEY_MIDDLENAME] ?? '',
143+
AddressInterface::KEY_PREFIX => $data[AddressInterface::KEY_PREFIX] ?? '',
144+
AddressInterface::KEY_SUFFIX => $data[AddressInterface::KEY_SUFFIX] ?? '',
145+
AddressInterface::KEY_VAT_ID => $data[AddressInterface::KEY_VAT_ID] ?? '',
146+
AddressInterface::KEY_COMPANY => $data[AddressInterface::KEY_COMPANY] ?? '',
147+
AddressInterface::KEY_TELEPHONE => $data[AddressInterface::KEY_TELEPHONE] ?? '',
148+
AddressInterface::KEY_CITY => $data[AddressInterface::KEY_CITY] ?? '',
149+
AddressInterface::KEY_STREET => $data[AddressInterface::KEY_STREET] ?? '',
150+
AddressInterface::KEY_POSTCODE => $data[AddressInterface::KEY_POSTCODE] ?? '',
151+
AddressInterface::KEY_FAX => $data[AddressInterface::KEY_FAX] ?? '',
152+
AddressInterface::CUSTOM_ATTRIBUTES => $data[AddressInterface::CUSTOM_ATTRIBUTES] ?? ''
153+
]);
154+
}
155+
foreach ($this->shipmentEstimation->estimateByExtendedAddress($cart->getId(), $address) as $method) {
156+
$shippingMethods[] = $this->formatMoneyTypeData->execute(
157+
$this->dataObjectConverter->toFlatArray($method, [], ShippingMethodInterface::class),
158+
$cart->getCurrency()->getQuoteCurrencyCode()
159+
);
160+
}
161+
return $shippingMethods;
162+
}
163+
}

0 commit comments

Comments
 (0)