Skip to content

Commit 25772e6

Browse files
🔃 [GraphQL] Partners Acceleration Program Contributions - 2.4-develop
Accepted Community Pull Requests: - #28890: #26121 - special price & tier price are coming in base currency (by @pmarjan) Fixed GitHub Issues: - #26121: special price & tier price are coming in base currency (reported by @mkum73) has been fixed in #28890 by @pmarjan in 2.4-develop branch Related commits: 1. 5c69aa1 2. cce0a99 3. 89b968a 4. 75f71a4 5. 4c5086a 6. 9bd5def 7. 7a526e9 8. d99ff1b 9. 831fb5b 10. f83763e 11. 20cd3d0 12. 9d80fa3
2 parents f39a2e0 + 86df5fc commit 25772e6

File tree

7 files changed

+385
-2
lines changed

7 files changed

+385
-2
lines changed

app/code/Magento/CatalogCustomerGraphQl/Model/Resolver/PriceTiers.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
1313
use Magento\Framework\Exception\LocalizedException;
1414
use Magento\Framework\GraphQl\Query\Resolver\ValueFactory;
15+
use Magento\Framework\Pricing\PriceCurrencyInterface;
1516
use Magento\CatalogCustomerGraphQl\Model\Resolver\Product\Price\Tiers;
1617
use Magento\CatalogCustomerGraphQl\Model\Resolver\Product\Price\TiersFactory;
1718
use Magento\CatalogCustomerGraphQl\Model\Resolver\Customer\GetCustomerGroup;
@@ -60,25 +61,33 @@ class PriceTiers implements ResolverInterface
6061
*/
6162
private $priceProviderPool;
6263

64+
/**
65+
* @var PriceCurrencyInterface
66+
*/
67+
private $priceCurrency;
68+
6369
/**
6470
* @param ValueFactory $valueFactory
6571
* @param TiersFactory $tiersFactory
6672
* @param GetCustomerGroup $getCustomerGroup
6773
* @param Discount $discount
6874
* @param PriceProviderPool $priceProviderPool
75+
* @param PriceCurrencyInterface $priceCurrency
6976
*/
7077
public function __construct(
7178
ValueFactory $valueFactory,
7279
TiersFactory $tiersFactory,
7380
GetCustomerGroup $getCustomerGroup,
7481
Discount $discount,
75-
PriceProviderPool $priceProviderPool
82+
PriceProviderPool $priceProviderPool,
83+
PriceCurrencyInterface $priceCurrency
7684
) {
7785
$this->valueFactory = $valueFactory;
7886
$this->tiersFactory = $tiersFactory;
7987
$this->getCustomerGroup = $getCustomerGroup;
8088
$this->discount = $discount;
8189
$this->priceProviderPool = $priceProviderPool;
90+
$this->priceCurrency = $priceCurrency;
8291
}
8392

8493
/**
@@ -130,6 +139,7 @@ private function formatProductTierPrices(array $tierPrices, float $productPrice,
130139
$tiers = [];
131140

132141
foreach ($tierPrices as $tierPrice) {
142+
$tierPrice->setValue($this->priceCurrency->convertAndRound($tierPrice->getValue()));
133143
$percentValue = $tierPrice->getExtensionAttributes()->getPercentageValue();
134144
if ($percentValue && is_numeric($percentValue)) {
135145
$discount = $this->discount->getDiscountByPercent($productPrice, (float)$percentValue);
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CatalogGraphQl\Model\Resolver\Product;
9+
10+
use Magento\Framework\GraphQl\Config\Element\Field;
11+
use Magento\Framework\GraphQl\Query\ResolverInterface;
12+
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
13+
use Magento\Catalog\Api\Data\ProductInterface;
14+
use Magento\Catalog\Pricing\Price\SpecialPrice as PricingSpecialPrice;
15+
16+
/**
17+
* Resolver for Special Price
18+
*/
19+
class SpecialPrice implements ResolverInterface
20+
{
21+
/**
22+
* @inheritdoc
23+
*/
24+
public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null)
25+
{
26+
/** @var ProductInterface $product */
27+
$product = $value['model'];
28+
/** @var PricingSpecialPrice $specialPrice */
29+
$specialPrice = $product->getPriceInfo()->getPrice(PricingSpecialPrice::PRICE_CODE);
30+
31+
if ($specialPrice->getValue()) {
32+
return $specialPrice->getValue();
33+
}
34+
35+
return null;
36+
}
37+
}

app/code/Magento/CatalogGraphQl/etc/schema.graphqls

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ interface ProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\
8888
sku: String @doc(description: "A number or code assigned to a product to identify the product, options, price, and manufacturer.")
8989
description: ComplexTextValue @doc(description: "Detailed information about the product. The value can include simple HTML tags.") @resolver(class: "\\Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductComplexTextAttribute")
9090
short_description: ComplexTextValue @doc(description: "A short description of the product. Its use depends on the theme.") @resolver(class: "\\Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductComplexTextAttribute")
91-
special_price: Float @doc(description: "The discounted price of the product.")
91+
special_price: Float @doc(description: "The discounted price of the product.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\SpecialPrice")
9292
special_from_date: String @doc(description: "The beginning date that a product has a special price.")
9393
special_to_date: String @doc(description: "The end date that a product has a special price.")
9494
attribute_set_id: Int @doc(description: "The attribute set assigned to the product.")
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\GraphQl\CatalogCustomer;
9+
10+
use Magento\GraphQl\GetCustomerAuthenticationHeader;
11+
use Magento\TestFramework\TestCase\GraphQlAbstract;
12+
use Magento\Store\Api\StoreRepositoryInterface;
13+
use Magento\TestFramework\Helper\Bootstrap;
14+
use Magento\TestFramework\ObjectManager;
15+
16+
class PriceTiersTest extends GraphQlAbstract
17+
{
18+
/**
19+
* @var ObjectManager
20+
*/
21+
private $objectManager;
22+
23+
/**
24+
* @var GetCustomerAuthenticationHeader
25+
*/
26+
private $getCustomerAuthenticationHeader;
27+
28+
protected function setUp(): void
29+
{
30+
$this->objectManager = Bootstrap::getObjectManager();
31+
$this->getCustomerAuthenticationHeader = $this->objectManager->get(GetCustomerAuthenticationHeader::class);
32+
}
33+
34+
/**
35+
* @magentoApiDataFixture Magento/Catalog/_files/product_simple.php
36+
*/
37+
public function testAllGroups()
38+
{
39+
$productSku = 'simple';
40+
$query = $this->getProductSearchQuery($productSku);
41+
42+
$response = $this->graphQlQuery($query);
43+
44+
$itemTiers = $response['products']['items'][0]['price_tiers'];
45+
$this->assertCount(5, $itemTiers);
46+
$this->assertEquals(8, $this->getValueForQuantity(2, $itemTiers));
47+
$this->assertEquals(5, $this->getValueForQuantity(3, $itemTiers));
48+
$this->assertEquals(6, $this->getValueForQuantity(3.2, $itemTiers));
49+
}
50+
51+
/**
52+
* @magentoApiDataFixture Magento/Customer/_files/customer.php
53+
* @magentoApiDataFixture Magento/Catalog/_files/simple_product_with_tier_prices_for_multiple_groups.php
54+
*/
55+
public function testLoggedInCustomer()
56+
{
57+
$productSku = 'simple';
58+
$query = $this->getProductSearchQuery($productSku);
59+
$response = $this->graphQlQuery(
60+
$query,
61+
[],
62+
'',
63+
$this->getCustomerAuthenticationHeader->execute('[email protected]', 'password')
64+
);
65+
66+
$itemTiers = $response['products']['items'][0]['price_tiers'];
67+
$this->assertCount(3, $itemTiers);
68+
$this->assertEquals(9.25, $this->getValueForQuantity(2, $itemTiers));
69+
$this->assertEquals(8.25, $this->getValueForQuantity(3, $itemTiers));
70+
$this->assertEquals(7.25, $this->getValueForQuantity(5, $itemTiers));
71+
}
72+
73+
/**
74+
* @magentoApiDataFixture Magento/Store/_files/second_store_with_second_currency.php
75+
* @magentoApiDataFixture Magento/Customer/_files/customer.php
76+
* @magentoApiDataFixture Magento/Catalog/_files/simple_product_with_tier_prices_for_multiple_groups.php
77+
*/
78+
public function testSecondStoreViewWithCurrencyRate()
79+
{
80+
$storeViewCode = 'fixture_second_store';
81+
$storeRepository = $this->objectManager->get(StoreRepositoryInterface::class);
82+
$rate = $storeRepository->get($storeViewCode)->getCurrentCurrencyRate();
83+
$productSku = 'simple';
84+
$query = $this->getProductSearchQuery($productSku);
85+
$headers = array_merge(
86+
$this->getCustomerAuthenticationHeader->execute('[email protected]', 'password'),
87+
$this->getHeaderStore($storeViewCode)
88+
);
89+
90+
$response = $this->graphQlQuery(
91+
$query,
92+
[],
93+
'',
94+
$headers
95+
);
96+
97+
$itemTiers = $response['products']['items'][0]['price_tiers'];
98+
$this->assertCount(3, $itemTiers);
99+
$this->assertEquals(round(9.25 * $rate, 2), $this->getValueForQuantity(2, $itemTiers));
100+
$this->assertEquals(round(8.25 * $rate, 2), $this->getValueForQuantity(3, $itemTiers));
101+
$this->assertEquals(round(7.25 * $rate, 2), $this->getValueForQuantity(5, $itemTiers));
102+
}
103+
104+
/**
105+
* Get the tier price value for the given product quantity
106+
*
107+
* @param float $quantity
108+
* @param array $tiers
109+
* @return float
110+
*/
111+
private function getValueForQuantity(float $quantity, array $tiers): float
112+
{
113+
$filteredResult = array_values(array_filter($tiers, function ($tier) use ($quantity) {
114+
if ((float)$tier['quantity'] == $quantity) {
115+
return $tier;
116+
}
117+
}));
118+
119+
return (float)$filteredResult[0]['final_price']['value'];
120+
}
121+
122+
/**
123+
* Get a query which user filter for product sku and returns price_tiers
124+
*
125+
* @param string $productSku
126+
* @return string
127+
*/
128+
private function getProductSearchQuery(string $productSku): string
129+
{
130+
return <<<QUERY
131+
{
132+
products(filter: {sku: {eq: "{$productSku}"}}) {
133+
items {
134+
price_tiers {
135+
final_price {
136+
currency
137+
value
138+
}
139+
discount {
140+
amount_off
141+
percent_off
142+
}
143+
quantity
144+
}
145+
}
146+
}
147+
}
148+
QUERY;
149+
}
150+
151+
/**
152+
* Get array that would be used in request header
153+
*
154+
* @param string $storeViewCode
155+
* @return array
156+
*/
157+
private function getHeaderStore(string $storeViewCode): array
158+
{
159+
return ['Store' => $storeViewCode];
160+
}
161+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\GraphQl\CatalogCustomer;
9+
10+
use Magento\TestFramework\TestCase\GraphQlAbstract;
11+
use Magento\Store\Api\StoreRepositoryInterface;
12+
use Magento\TestFramework\Helper\Bootstrap;
13+
use Magento\TestFramework\ObjectManager;
14+
15+
class SpecialPriceTest extends GraphQlAbstract
16+
{
17+
/**
18+
* @var ObjectManager
19+
*/
20+
private $objectManager;
21+
22+
protected function setUp(): void
23+
{
24+
$this->objectManager = Bootstrap::getObjectManager();
25+
}
26+
27+
/**
28+
* @magentoApiDataFixture Magento/Catalog/_files/product_special_price.php
29+
*/
30+
public function testSpecialPrice()
31+
{
32+
$productSku = 'simple';
33+
$query = $this->getProductSearchQuery($productSku);
34+
35+
$response = $this->graphQlQuery($query);
36+
37+
$specialPrice = (float)$response['products']['items'][0]['special_price'];
38+
$this->assertEquals(5.99, $specialPrice);
39+
}
40+
41+
/**
42+
* @magentoApiDataFixture Magento/Store/_files/second_store_with_second_currency.php
43+
* @magentoApiDataFixture Magento/Catalog/_files/product_special_price.php
44+
*/
45+
public function testSpecialPriceWithCurrencyRate()
46+
{
47+
$storeViewCode = 'fixture_second_store';
48+
$storeRepository = $this->objectManager->get(StoreRepositoryInterface::class);
49+
$rate = $storeRepository->get($storeViewCode)->getCurrentCurrencyRate();
50+
$productSku = 'simple';
51+
$query = $this->getProductSearchQuery($productSku);
52+
$headers = $this->getHeaderStore($storeViewCode);
53+
54+
$response = $this->graphQlQuery(
55+
$query,
56+
[],
57+
'',
58+
$headers
59+
);
60+
61+
$specialPrice = (float)$response['products']['items'][0]['special_price'];
62+
$this->assertEquals(round(5.99 * $rate, 2), $specialPrice);
63+
}
64+
65+
/**
66+
* Get a query which user filter for product sku and returns special_price
67+
*
68+
* @param string $productSku
69+
* @return string
70+
*/
71+
private function getProductSearchQuery(string $productSku): string
72+
{
73+
return <<<QUERY
74+
{
75+
products(filter: {sku: {eq: "{$productSku}"}}) {
76+
items {
77+
special_price
78+
}
79+
}
80+
}
81+
QUERY;
82+
}
83+
84+
/**
85+
* Get array that would be used in request header
86+
*
87+
* @param string $storeViewCode
88+
* @return array
89+
*/
90+
private function getHeaderStore(string $storeViewCode): array
91+
{
92+
return ['Store' => $storeViewCode];
93+
}
94+
}

0 commit comments

Comments
 (0)