Skip to content

Commit 643eb35

Browse files
Merge remote-tracking branch '40003/fix-for-issue-39959' into julprs
2 parents 6dd3fa9 + a19223c commit 643eb35

File tree

3 files changed

+208
-5
lines changed

3 files changed

+208
-5
lines changed

app/code/Magento/Catalog/Observer/SetSpecialPriceStartDate.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,14 @@ public function execute(\Magento\Framework\Event\Observer $observer)
3939
/** @var $product \Magento\Catalog\Model\Product */
4040
$product = $observer->getEvent()->getProduct();
4141
if ($product->getSpecialPrice() && $product->getSpecialFromDate() === null) {
42-
$product->setData('special_from_date', $this->localeDate->date()->setTime(0, 0));
42+
// Set the special_from_date to the current date with time 00:00:00 when a special price is defined
43+
// but no start date is specified. This ensures the special price takes effect immediately
44+
// and is consistent with how the special price validation works in Magento.
45+
// The time is explicitly set to midnight to ensure the special price is active for the entire day.
46+
$product->setData(
47+
'special_from_date',
48+
$this->localeDate->date()->setTime(0, 0)->format('Y-m-d H:i:s')
49+
);
4350
}
4451
return $this;
4552
}

app/code/Magento/Catalog/Test/Unit/Observer/SetSpecialPriceStartDateTest.php

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,13 @@ protected function setUp(): void
8888
}
8989

9090
/**
91-
* Test observer execute method
91+
* Test observer execute method when special_from_date is null
9292
*/
9393
public function testExecuteModifySpecialFromDate(): void
9494
{
9595
$specialPrice = 15;
9696
$specialFromDate = null;
97-
$localeDateMock = ['special_from_date' => $this->returnValue($this->dateObject)];
97+
$formattedDate = '2023-01-01 00:00:00';
9898

9999
$this->observerMock
100100
->expects($this->once())
@@ -106,10 +106,18 @@ public function testExecuteModifySpecialFromDate(): void
106106
->method('getProduct')
107107
->willReturn($this->productMock);
108108

109-
$this->dateObject->expects($this->any())
109+
$this->dateObject
110+
->expects($this->once())
110111
->method('setTime')
112+
->with(0, 0)
111113
->willReturnSelf();
112114

115+
$this->dateObject
116+
->expects($this->once())
117+
->method('format')
118+
->with('Y-m-d H:i:s')
119+
->willReturn($formattedDate);
120+
113121
$this->timezone
114122
->expects($this->once())
115123
->method('date')
@@ -128,7 +136,79 @@ public function testExecuteModifySpecialFromDate(): void
128136
$this->productMock
129137
->expects($this->once())
130138
->method('setData')
131-
->willReturn($localeDateMock);
139+
->with('special_from_date', $formattedDate);
140+
141+
$this->observer->execute($this->observerMock);
142+
}
143+
144+
/**
145+
* Test observer doesn't modify special_from_date when it's already set
146+
*/
147+
public function testExecuteDoesNotModifyExistingSpecialFromDate(): void
148+
{
149+
$specialPrice = 15;
150+
$existingSpecialFromDate = '2023-01-01 00:00:00';
151+
152+
$this->observerMock
153+
->expects($this->once())
154+
->method('getEvent')
155+
->willReturn($this->eventMock);
156+
157+
$this->eventMock
158+
->expects($this->once())
159+
->method('getProduct')
160+
->willReturn($this->productMock);
161+
162+
$this->productMock
163+
->expects($this->once())
164+
->method('getSpecialPrice')
165+
->willReturn($specialPrice);
166+
167+
$this->productMock
168+
->expects($this->once())
169+
->method('getSpecialFromDate')
170+
->willReturn($existingSpecialFromDate);
171+
172+
$this->productMock
173+
->expects($this->never())
174+
->method('setData');
175+
176+
$this->timezone
177+
->expects($this->never())
178+
->method('date');
179+
180+
$this->observer->execute($this->observerMock);
181+
}
182+
183+
/**
184+
* Test observer doesn't set special_from_date when special price is not set
185+
*/
186+
public function testExecuteDoesNotSetSpecialFromDateWithoutSpecialPrice(): void
187+
{
188+
$specialPrice = null;
189+
190+
$this->observerMock
191+
->expects($this->once())
192+
->method('getEvent')
193+
->willReturn($this->eventMock);
194+
195+
$this->eventMock
196+
->expects($this->once())
197+
->method('getProduct')
198+
->willReturn($this->productMock);
199+
200+
$this->productMock
201+
->expects($this->once())
202+
->method('getSpecialPrice')
203+
->willReturn($specialPrice);
204+
205+
$this->productMock
206+
->expects($this->never())
207+
->method('getSpecialFromDate');
208+
209+
$this->productMock
210+
->expects($this->never())
211+
->method('setData');
132212

133213
$this->observer->execute($this->observerMock);
134214
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* * All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Catalog\Model\Product;
9+
10+
use Magento\Catalog\Api\ProductRepositoryInterface;
11+
use Magento\Framework\App\ObjectManager;
12+
use Magento\Framework\ObjectManagerInterface;
13+
use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
14+
use Magento\TestFramework\Helper\Bootstrap;
15+
use PHPUnit\Framework\TestCase;
16+
17+
/**
18+
* Integration test for special price functionality
19+
*/
20+
class SpecialPriceTest extends TestCase
21+
{
22+
/**
23+
* @var ObjectManager
24+
*/
25+
private ObjectManager $objectManager;
26+
27+
/**
28+
* @var ProductRepositoryInterface
29+
*/
30+
private mixed $productRepository;
31+
32+
/**
33+
* @var TimezoneInterface
34+
*/
35+
private mixed $localeDate;
36+
37+
/**
38+
* Set up
39+
*/
40+
protected function setUp(): void
41+
{
42+
$this->objectManager = Bootstrap::getObjectManager();
43+
$this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class);
44+
$this->localeDate = $this->objectManager->get(TimezoneInterface::class);
45+
}
46+
47+
/**
48+
* Test that special_from_date is automatically set when special price is set via API
49+
*
50+
* @magentoDbIsolation enabled
51+
* @magentoAppIsolation enabled
52+
* @magentoDataFixture Magento/Catalog/_files/product_simple.php
53+
*/
54+
public function testSpecialFromDateSetWhenSpecialPriceSet(): void
55+
{
56+
$product = $this->productRepository->get('simple');
57+
$product->setSpecialPrice(5.99);
58+
$product->setSpecialFromDate(null);
59+
60+
$this->productRepository->save($product);
61+
$updatedProduct = $this->productRepository->get('simple', false, null, true);
62+
$this->assertNotNull($updatedProduct->getSpecialFromDate());
63+
$expectedDate = $this->localeDate->date()->setTime(0, 0, 0)->format('Y-m-d');
64+
$actualDate = substr($updatedProduct->getSpecialFromDate(), 0, 10);
65+
66+
// Assert special_from_date is set to current date with time 00:00:00
67+
$this->assertEquals($expectedDate, $actualDate);
68+
}
69+
70+
/**
71+
* Test that existing special_from_date is not changed when product is saved
72+
*
73+
* @magentoDbIsolation enabled
74+
* @magentoAppIsolation enabled
75+
* @magentoDataFixture Magento/Catalog/_files/product_simple.php
76+
*/
77+
public function testExistingSpecialFromDateNotChanged(): void
78+
{
79+
$product = $this->productRepository->get('simple');
80+
81+
$specificDate = '2023-01-01 00:00:00';
82+
$product->setSpecialPrice(5.99);
83+
$product->setSpecialFromDate($specificDate);
84+
85+
$this->productRepository->save($product);
86+
$updatedProduct = $this->productRepository->get('simple', false, null, true);
87+
88+
// Assert special_from_date remains unchanged
89+
$this->assertEquals($specificDate, $updatedProduct->getSpecialFromDate());
90+
}
91+
92+
/**
93+
* Test that special price is correctly applied when special_from_date is today
94+
*
95+
* @magentoDbIsolation enabled
96+
* @magentoAppIsolation enabled
97+
* @magentoDataFixture Magento/Catalog/_files/product_simple.php
98+
*/
99+
public function testSpecialPriceAppliedWithTodayDate(): void
100+
{
101+
$product = $this->productRepository->get('simple');
102+
$regularPrice = 10.00;
103+
$specialPrice = 5.99;
104+
105+
$product->setPrice($regularPrice);
106+
$today = $this->localeDate->date()->format('Y-m-d 00:00:00');
107+
$product->setSpecialPrice($specialPrice);
108+
$product->setSpecialFromDate($today);
109+
110+
$this->productRepository->save($product);
111+
$updatedProduct = $this->productRepository->get('simple', false, null, true);
112+
113+
// Assert special price is applied
114+
$this->assertEquals($specialPrice, $updatedProduct->getFinalPrice());
115+
}
116+
}

0 commit comments

Comments
 (0)