Skip to content

Commit ef2bf4f

Browse files
committed
Merge remote-tracking branch 'origin/AC-14049' into spartans_pr_26082025
2 parents 0c1d452 + 10f4e1f commit ef2bf4f

File tree

4 files changed

+569
-0
lines changed

4 files changed

+569
-0
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Sales\Plugin\Model;
9+
10+
use Magento\Framework\Exception\LocalizedException;
11+
use Magento\Sales\Api\Data\OrderInterface;
12+
use Magento\Sales\Model\OrderRepository;
13+
14+
/**
15+
* Plugin for OrderRepository to add comprehensive order validation
16+
*/
17+
class OrderRepositoryPlugin
18+
{
19+
/**
20+
* Validate order before save
21+
*
22+
* @param OrderRepository $subject
23+
* @param OrderInterface $entity
24+
* @return array
25+
* @throws LocalizedException
26+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
27+
*/
28+
public function beforeSave(OrderRepository $subject, OrderInterface $entity): array
29+
{
30+
$this->validateBillingAddress($entity);
31+
$this->validateOrderItems($entity);
32+
return [$entity];
33+
}
34+
35+
/**
36+
* Validate billing address exists and has required fields
37+
*
38+
* @param OrderInterface $entity
39+
* @throws LocalizedException
40+
*/
41+
private function validateBillingAddress(OrderInterface $entity): void
42+
{
43+
$billingAddress = $entity->getBillingAddress();
44+
if (!$billingAddress ||
45+
!$billingAddress->getFirstname() ||
46+
!$billingAddress->getLastname()) {
47+
throw new LocalizedException(__('Please provide billing address for the order.'));
48+
}
49+
}
50+
51+
/**
52+
* Validate order has items
53+
*
54+
* @param OrderInterface $entity
55+
* @throws LocalizedException
56+
*/
57+
private function validateOrderItems(OrderInterface $entity): void
58+
{
59+
$items = $entity->getAllVisibleItems();
60+
61+
if (!$items || count($items) === 0) {
62+
throw new LocalizedException(__('Please specify order items.'));
63+
}
64+
}
65+
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Sales\Test\Unit\Plugin\Model;
9+
10+
use Magento\Framework\Exception\LocalizedException;
11+
use Magento\Sales\Api\Data\OrderAddressInterface;
12+
use Magento\Sales\Api\Data\OrderItemInterface;
13+
use Magento\Sales\Model\Order;
14+
use Magento\Sales\Model\OrderRepository;
15+
use PHPUnit\Framework\MockObject\MockObject;
16+
use PHPUnit\Framework\TestCase;
17+
use Magento\Sales\Plugin\Model\OrderRepositoryPlugin;
18+
19+
/**
20+
* Unit test for OrderRepositoryPlugin
21+
*/
22+
class OrderRepositoryPluginTest extends TestCase
23+
{
24+
/**
25+
* @var OrderRepositoryPlugin
26+
*/
27+
private $plugin;
28+
29+
/**
30+
* @var OrderRepository|MockObject
31+
*/
32+
private $orderRepository;
33+
34+
/**
35+
* @var Order|MockObject
36+
*/
37+
private $order;
38+
39+
/**
40+
* @var OrderAddressInterface|MockObject
41+
*/
42+
private $billingAddress;
43+
44+
/**
45+
* @var OrderItemInterface|MockObject
46+
*/
47+
private $orderItem;
48+
49+
protected function setUp(): void
50+
{
51+
$this->plugin = new OrderRepositoryPlugin();
52+
$this->orderRepository = $this->createMock(OrderRepository::class);
53+
$this->order = $this->createMock(Order::class);
54+
$this->billingAddress = $this->createMock(OrderAddressInterface::class);
55+
$this->orderItem = $this->createMock(OrderItemInterface::class);
56+
}
57+
58+
/**
59+
* Test successful validation with valid order
60+
*/
61+
public function testBeforeSaveWithValidOrder(): void
62+
{
63+
// Setup billing address
64+
$this->billingAddress->method('getFirstname')->willReturn('John');
65+
$this->billingAddress->method('getLastname')->willReturn('Doe');
66+
$this->billingAddress->method('getStreet')->willReturn(['123 Main St']);
67+
$this->billingAddress->method('getCity')->willReturn('City');
68+
$this->billingAddress->method('getCountryId')->willReturn('US');
69+
70+
// Setup order item
71+
$this->orderItem->method('getProductId')->willReturn(1);
72+
$this->orderItem->method('getSku')->willReturn('simple-product');
73+
$this->orderItem->method('getQtyOrdered')->willReturn(2);
74+
75+
// Setup order
76+
$this->order->method('getBillingAddress')->willReturn($this->billingAddress);
77+
$this->order->method('getAllVisibleItems')->willReturn([$this->orderItem]);
78+
79+
$result = $this->plugin->beforeSave($this->orderRepository, $this->order);
80+
81+
$this->assertEquals([$this->order], $result);
82+
}
83+
84+
/**
85+
* Test validation fails with missing billing address
86+
*/
87+
public function testBeforeSaveWithMissingBillingAddress(): void
88+
{
89+
$this->order->method('getBillingAddress')->willReturn(null);
90+
91+
$this->expectException(LocalizedException::class);
92+
$this->expectExceptionMessage('Please provide billing address for the order.');
93+
94+
$this->plugin->beforeSave($this->orderRepository, $this->order);
95+
}
96+
97+
/**
98+
* Test validation fails with incomplete billing address
99+
*/
100+
public function testBeforeSaveWithIncompleteBillingAddress(): void
101+
{
102+
$this->billingAddress->method('getFirstname')->willReturn('');
103+
$this->billingAddress->method('getLastname')->willReturn('Doe');
104+
105+
$this->order->method('getBillingAddress')->willReturn($this->billingAddress);
106+
107+
$this->expectException(LocalizedException::class);
108+
$this->expectExceptionMessage('Please provide billing address for the order.');
109+
110+
$this->plugin->beforeSave($this->orderRepository, $this->order);
111+
}
112+
113+
/**
114+
* Test validation fails with missing order items
115+
*/
116+
public function testBeforeSaveWithMissingItems(): void
117+
{
118+
// Setup valid billing address
119+
$this->billingAddress->method('getFirstname')->willReturn('John');
120+
$this->billingAddress->method('getLastname')->willReturn('Doe');
121+
$this->billingAddress->method('getStreet')->willReturn(['123 Main St']);
122+
$this->billingAddress->method('getCity')->willReturn('City');
123+
$this->billingAddress->method('getCountryId')->willReturn('US');
124+
125+
$this->order->method('getBillingAddress')->willReturn($this->billingAddress);
126+
$this->order->method('getAllVisibleItems')->willReturn([]);
127+
128+
$this->expectException(LocalizedException::class);
129+
$this->expectExceptionMessage('Please specify order items.');
130+
131+
$this->plugin->beforeSave($this->orderRepository, $this->order);
132+
}
133+
}

app/code/Magento/Sales/etc/webapi_rest/di.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,9 @@
2626
<plugin name="add_existing_product_options"
2727
type="Magento\Sales\Plugin\Model\ResourceModel\Order\Relation\AddExistingItemProductOptions"/>
2828
</type>
29+
<type name="Magento\Sales\Model\OrderRepository">
30+
<plugin name="validate_billing_address_on_save"
31+
type="Magento\Sales\Plugin\Model\OrderRepositoryPlugin"
32+
sortOrder="10"/>
33+
</type>
2934
</config>

0 commit comments

Comments
 (0)