Skip to content

Commit f5464ec

Browse files
committed
2 parents 55ca489 + b1a6e67 commit f5464ec

3 files changed

Lines changed: 108 additions & 1 deletion

File tree

ProductPriceDataExporter/Model/Query/CustomerGroupPricesQuery.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,9 @@ private function getCatalogRulePricesSelect(array $productIds): Select
191191
->joinInner(
192192
['rule' => $this->resourceConnection->getTableName('catalogrule_product_price')],
193193
'product.entity_id = rule.product_id' .
194-
' AND rule.website_id = website.website_id AND rule.rule_date = ' . $this->getWebsiteDate(),
194+
' AND rule.website_id = website.website_id' .
195+
' AND rule.rule_date IN (' . $this->getWebsiteDate()
196+
. ", '" . $this->dateWebsiteProvider->getDefaultScopeDate() . "')",
195197
[]
196198
)->columns([
197199
'price' => new \Zend_Db_Expr('NULL'),

ProductPriceDataExporter/Model/Query/DateWebsiteProvider.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Magento\Framework\App\ResourceConnection;
1313
use Magento\Framework\Stdlib\DateTime;
1414
use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
15+
use Magento\Store\Model\Store;
1516

1617
/**
1718
* Get _current_ date in website timezone in format [website_id => website_date, ...]
@@ -85,4 +86,16 @@ public function getWebsitesDate(): array
8586
}
8687
return $this->data;
8788
}
89+
90+
/**
91+
* Return the current date in the default scope timezone.
92+
*
93+
* Matches the indexer's rule_date when useWebsiteTimezone=false.
94+
*
95+
* @return string
96+
*/
97+
public function getDefaultScopeDate(): string
98+
{
99+
return $this->dateTime->formatDate($this->localeDate->scopeTimeStamp(Store::DEFAULT_STORE_ID), false);
100+
}
88101
}

ProductPriceDataExporter/Test/Integration/ExportSingleProductPriceTest.php

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
use Magento\CatalogRule\Api\Data\RuleInterface;
1212
use Magento\CatalogRule\Model\Indexer\Rule\RuleProductProcessor;
1313
use Magento\CatalogRule\Model\ResourceModel\RuleFactory as ResourceRuleFactory;
14+
use Magento\Framework\App\ResourceConnection;
15+
use Magento\Indexer\Model\Indexer;
16+
use Magento\ProductPriceDataExporter\Model\Query\DateWebsiteProvider;
1417
use Magento\Framework\Exception\CouldNotSaveException;
1518
use Magento\Framework\Exception\InputException;
1619
use Magento\Framework\Exception\LocalizedException;
@@ -119,6 +122,95 @@ public function testExportSimpleProductsWithDisabledCatalogPriceRulePrices(array
119122
$this->checkExpectedItemsAreExportedInFeed($expectedSimpleProductPrices);
120123
}
121124

125+
/**
126+
* Catalog rule price must still be exported for a website whose local date differs from the
127+
* default scope date, when catalogrule_product_price holds only the default-scope date - the
128+
* state left by the indexer with useWebsiteTimezone=false (CatalogRuleStaging) after midnight
129+
* UTC for UTC-negative stores.
130+
*
131+
* The default scope timezone (Kiritimati, UTC+14) and the base store timezone (Pago_Pago,
132+
* UTC-11) are 25h apart, so the website-local date is always different from the default scope
133+
* date and the export has to fall back to the default scope date to find the rule price.
134+
*
135+
* getDefaultScopeDate() reads the timezone via store scope 0 (the admin store), so the
136+
* config fixture must also be applied to admin_store - a plain default-scope fixture only
137+
* patches the "default" config branch and leaves the admin store's cached value stale.
138+
*
139+
* @magentoConfigFixture general/locale/timezone Pacific/Kiritimati
140+
* @magentoConfigFixture admin_store general/locale/timezone Pacific/Kiritimati
141+
* @magentoConfigFixture default_store general/locale/timezone Pacific/Pago_Pago
142+
* @magentoDataFixture Magento_ProductPriceDataExporter::Test/_files/simple_products.php
143+
* @magentoDataFixture Magento/CatalogRule/_files/catalog_rule_25_customer_group_all.php
144+
* @throws NoSuchEntityException
145+
*/
146+
public function testExportCatalogRulePriceWhenWebsiteDateDiffersFromDefaultScope(): void
147+
{
148+
$objectManager = Bootstrap::getObjectManager();
149+
$resourceConnection = $objectManager->get(ResourceConnection::class);
150+
$dateProvider = $objectManager->get(DateWebsiteProvider::class);
151+
152+
$baseWebsiteId = (int)$this->websiteRepository->get('base')->getId();
153+
$defaultScopeDate = $dateProvider->getDefaultScopeDate();
154+
$websiteDate = $dateProvider->getWebsitesDate()[$baseWebsiteId] ?? null;
155+
156+
// Guard the test premise: with a >24h timezone spread these must never be equal,
157+
// otherwise the default-scope fallback would not be exercised.
158+
self::assertNotSame(
159+
$defaultScopeDate,
160+
$websiteDate,
161+
'Website local date must differ from the default scope date for this test to be meaningful'
162+
);
163+
164+
// Reproduce useWebsiteTimezone=false state: the rule price exists only under the
165+
// default-scope date, not under the base website's local date.
166+
$productId = (int)$this->productRepository->get('simple_product_with_regular_price')->getId();
167+
$connection = $resourceConnection->getConnection();
168+
$table = $resourceConnection->getTableName('catalogrule_product_price');
169+
$connection->delete($table, ['product_id = ?' => $productId]);
170+
$connection->insert($table, [
171+
'rule_date' => $defaultScopeDate,
172+
'customer_group_id' => 0,
173+
'product_id' => $productId,
174+
'rule_price' => self::getPriceForVersion(41.6625),
175+
'website_id' => $baseWebsiteId,
176+
]);
177+
178+
// Force the price feed to be rebuilt from the manipulated rule price table.
179+
$feedIndexer = $objectManager->create(Indexer::class);
180+
$feedIndexer->load('catalog_data_exporter_product_prices');
181+
$feedIndexer->invalidate();
182+
183+
$this->checkExpectedItemsAreExportedInFeed([
184+
'simple_product_with_regular_price_base_0' => [
185+
'sku' => 'simple_product_with_regular_price',
186+
'type' => 'SIMPLE',
187+
'customerGroupCode' => '0',
188+
'websiteCode' => 'base',
189+
'regular' => 55.55,
190+
'discounts' => null,
191+
'deleted' => false
192+
],
193+
'simple_product_with_regular_price_base_b6589fc6ab0dc82cf12099d1c2d40ab994e8410c' => [
194+
'sku' => 'simple_product_with_regular_price',
195+
'type' => 'SIMPLE',
196+
'customerGroupCode' => 'b6589fc6ab0dc82cf12099d1c2d40ab994e8410c',
197+
'websiteCode' => 'base',
198+
'regular' => 55.55,
199+
'discounts' => [0 => ['code' => 'catalog_rule', 'price' => self::getPriceForVersion(41.6625)]],
200+
'deleted' => false
201+
],
202+
'simple_product_with_regular_price_test_0' => [
203+
'sku' => 'simple_product_with_regular_price',
204+
'type' => 'SIMPLE',
205+
'customerGroupCode' => '0',
206+
'websiteCode' => 'test',
207+
'regular' => 55.55,
208+
'discounts' => null,
209+
'deleted' => false
210+
],
211+
]);
212+
}
213+
122214
/**
123215
* @magentoDataFixture Magento_ProductPriceDataExporter::Test/_files/downloadable_products.php
124216
* @param array $expectedDownloadableProductPricesDataProvider

0 commit comments

Comments
 (0)