diff --git a/app/code/Magento/Catalog/Observer/SetSpecialPriceStartDate.php b/app/code/Magento/Catalog/Observer/SetSpecialPriceStartDate.php index 280357069f967..e52913775692c 100644 --- a/app/code/Magento/Catalog/Observer/SetSpecialPriceStartDate.php +++ b/app/code/Magento/Catalog/Observer/SetSpecialPriceStartDate.php @@ -39,7 +39,14 @@ public function execute(\Magento\Framework\Event\Observer $observer) /** @var $product \Magento\Catalog\Model\Product */ $product = $observer->getEvent()->getProduct(); if ($product->getSpecialPrice() && $product->getSpecialFromDate() === null) { - $product->setData('special_from_date', $this->localeDate->date()->setTime(0, 0)); + // Set the special_from_date to the current date with time 00:00:00 when a special price is defined + // but no start date is specified. This ensures the special price takes effect immediately + // and is consistent with how the special price validation works in Magento. + // The time is explicitly set to midnight to ensure the special price is active for the entire day. + $product->setData( + 'special_from_date', + $this->localeDate->date()->setTime(0, 0)->format('Y-m-d H:i:s') + ); } return $this; } diff --git a/app/code/Magento/Catalog/Test/Unit/Observer/SetSpecialPriceStartDateTest.php b/app/code/Magento/Catalog/Test/Unit/Observer/SetSpecialPriceStartDateTest.php index b5c9c637f4311..bdd4867c57e9b 100644 --- a/app/code/Magento/Catalog/Test/Unit/Observer/SetSpecialPriceStartDateTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Observer/SetSpecialPriceStartDateTest.php @@ -88,13 +88,13 @@ protected function setUp(): void } /** - * Test observer execute method + * Test observer execute method when special_from_date is null */ public function testExecuteModifySpecialFromDate(): void { $specialPrice = 15; $specialFromDate = null; - $localeDateMock = ['special_from_date' => $this->returnValue($this->dateObject)]; + $formattedDate = '2023-01-01 00:00:00'; $this->observerMock ->expects($this->once()) @@ -106,10 +106,18 @@ public function testExecuteModifySpecialFromDate(): void ->method('getProduct') ->willReturn($this->productMock); - $this->dateObject->expects($this->any()) + $this->dateObject + ->expects($this->once()) ->method('setTime') + ->with(0, 0) ->willReturnSelf(); + $this->dateObject + ->expects($this->once()) + ->method('format') + ->with('Y-m-d H:i:s') + ->willReturn($formattedDate); + $this->timezone ->expects($this->once()) ->method('date') @@ -128,7 +136,79 @@ public function testExecuteModifySpecialFromDate(): void $this->productMock ->expects($this->once()) ->method('setData') - ->willReturn($localeDateMock); + ->with('special_from_date', $formattedDate); + + $this->observer->execute($this->observerMock); + } + + /** + * Test observer doesn't modify special_from_date when it's already set + */ + public function testExecuteDoesNotModifyExistingSpecialFromDate(): void + { + $specialPrice = 15; + $existingSpecialFromDate = '2023-01-01 00:00:00'; + + $this->observerMock + ->expects($this->once()) + ->method('getEvent') + ->willReturn($this->eventMock); + + $this->eventMock + ->expects($this->once()) + ->method('getProduct') + ->willReturn($this->productMock); + + $this->productMock + ->expects($this->once()) + ->method('getSpecialPrice') + ->willReturn($specialPrice); + + $this->productMock + ->expects($this->once()) + ->method('getSpecialFromDate') + ->willReturn($existingSpecialFromDate); + + $this->productMock + ->expects($this->never()) + ->method('setData'); + + $this->timezone + ->expects($this->never()) + ->method('date'); + + $this->observer->execute($this->observerMock); + } + + /** + * Test observer doesn't set special_from_date when special price is not set + */ + public function testExecuteDoesNotSetSpecialFromDateWithoutSpecialPrice(): void + { + $specialPrice = null; + + $this->observerMock + ->expects($this->once()) + ->method('getEvent') + ->willReturn($this->eventMock); + + $this->eventMock + ->expects($this->once()) + ->method('getProduct') + ->willReturn($this->productMock); + + $this->productMock + ->expects($this->once()) + ->method('getSpecialPrice') + ->willReturn($specialPrice); + + $this->productMock + ->expects($this->never()) + ->method('getSpecialFromDate'); + + $this->productMock + ->expects($this->never()) + ->method('setData'); $this->observer->execute($this->observerMock); } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/SpecialPriceTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/SpecialPriceTest.php new file mode 100644 index 0000000000000..e28bdca5d233e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/SpecialPriceTest.php @@ -0,0 +1,116 @@ +objectManager = Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->localeDate = $this->objectManager->get(TimezoneInterface::class); + } + + /** + * Test that special_from_date is automatically set when special price is set via API + * + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + */ + public function testSpecialFromDateSetWhenSpecialPriceSet(): void + { + $product = $this->productRepository->get('simple'); + $product->setSpecialPrice(5.99); + $product->setSpecialFromDate(null); + + $this->productRepository->save($product); + $updatedProduct = $this->productRepository->get('simple', false, null, true); + $this->assertNotNull($updatedProduct->getSpecialFromDate()); + $expectedDate = $this->localeDate->date()->setTime(0, 0, 0)->format('Y-m-d'); + $actualDate = substr($updatedProduct->getSpecialFromDate(), 0, 10); + + // Assert special_from_date is set to current date with time 00:00:00 + $this->assertEquals($expectedDate, $actualDate); + } + + /** + * Test that existing special_from_date is not changed when product is saved + * + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + */ + public function testExistingSpecialFromDateNotChanged(): void + { + $product = $this->productRepository->get('simple'); + + $specificDate = '2023-01-01 00:00:00'; + $product->setSpecialPrice(5.99); + $product->setSpecialFromDate($specificDate); + + $this->productRepository->save($product); + $updatedProduct = $this->productRepository->get('simple', false, null, true); + + // Assert special_from_date remains unchanged + $this->assertEquals($specificDate, $updatedProduct->getSpecialFromDate()); + } + + /** + * Test that special price is correctly applied when special_from_date is today + * + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Catalog/_files/product_simple.php + */ + public function testSpecialPriceAppliedWithTodayDate(): void + { + $product = $this->productRepository->get('simple'); + $regularPrice = 10.00; + $specialPrice = 5.99; + + $product->setPrice($regularPrice); + $today = $this->localeDate->date()->format('Y-m-d 00:00:00'); + $product->setSpecialPrice($specialPrice); + $product->setSpecialFromDate($today); + + $this->productRepository->save($product); + $updatedProduct = $this->productRepository->get('simple', false, null, true); + + // Assert special price is applied + $this->assertEquals($specialPrice, $updatedProduct->getFinalPrice()); + } +}