Skip to content

Commit 4e0f152

Browse files
author
Stanislav Idolov
authored
Merge pull request magento#3142 from arnobsh/MC-35306
MC-35306 Incorrect qty is returned for "Backorders with Notify Customer"
2 parents cd363c5 + b652c7f commit 4e0f152

File tree

2 files changed

+109
-15
lines changed

2 files changed

+109
-15
lines changed

InventorySales/Model/IsProductSalableCondition/BackOrderNotifyCustomerCondition.php

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,18 @@
77

88
namespace Magento\InventorySales\Model\IsProductSalableCondition;
99

10+
use Magento\Framework\App\ObjectManager;
1011
use Magento\InventoryConfigurationApi\Api\Data\StockItemConfigurationInterface;
1112
use Magento\InventoryConfigurationApi\Api\GetStockItemConfigurationInterface;
1213
use Magento\InventorySalesApi\Api\Data\ProductSalabilityErrorInterfaceFactory;
1314
use Magento\InventorySalesApi\Api\Data\ProductSalableResultInterface;
1415
use Magento\InventorySalesApi\Api\Data\ProductSalableResultInterfaceFactory;
1516
use Magento\InventorySalesApi\Api\IsProductSalableForRequestedQtyInterface;
1617
use Magento\InventorySalesApi\Model\GetStockItemDataInterface;
18+
use Magento\InventorySalesApi\Api\GetProductSalableQtyInterface;
1719

1820
/**
19-
* @inheritdoc
21+
* Get back order notify for customer condition
2022
*/
2123
class BackOrderNotifyCustomerCondition implements IsProductSalableForRequestedQtyInterface
2224
{
@@ -40,22 +42,31 @@ class BackOrderNotifyCustomerCondition implements IsProductSalableForRequestedQt
4042
*/
4143
private $productSalabilityErrorFactory;
4244

45+
/**
46+
* @var GetProductSalableQtyInterface
47+
*/
48+
private $getProductSalableQty;
49+
4350
/**
4451
* @param GetStockItemConfigurationInterface $getStockItemConfiguration
4552
* @param GetStockItemDataInterface $getStockItemData
4653
* @param ProductSalableResultInterfaceFactory $productSalableResultFactory
4754
* @param ProductSalabilityErrorInterfaceFactory $productSalabilityErrorFactory
55+
* @param GetProductSalableQtyInterface|null $getProductSalableQty
4856
*/
4957
public function __construct(
5058
GetStockItemConfigurationInterface $getStockItemConfiguration,
5159
GetStockItemDataInterface $getStockItemData,
5260
ProductSalableResultInterfaceFactory $productSalableResultFactory,
53-
ProductSalabilityErrorInterfaceFactory $productSalabilityErrorFactory
61+
ProductSalabilityErrorInterfaceFactory $productSalabilityErrorFactory,
62+
?GetProductSalableQtyInterface $getProductSalableQty = null
5463
) {
5564
$this->getStockItemConfiguration = $getStockItemConfiguration;
5665
$this->getStockItemData = $getStockItemData;
5766
$this->productSalableResultFactory = $productSalableResultFactory;
5867
$this->productSalabilityErrorFactory = $productSalabilityErrorFactory;
68+
$this->getProductSalableQty = $getProductSalableQty
69+
?? ObjectManager::getInstance()->get(GetProductSalableQtyInterface::class);
5970
}
6071

6172
/**
@@ -73,15 +84,18 @@ public function execute(string $sku, int $stockId, float $requestedQty): Product
7384
return $this->productSalableResultFactory->create(['errors' => []]);
7485
}
7586

76-
$backOrderQty = $requestedQty - $stockItemData[GetStockItemDataInterface::QUANTITY];
77-
if ($backOrderQty > 0) {
87+
$salableQty = $this->getProductSalableQty->execute($sku, $stockId);
88+
$backOrderQty = $requestedQty - $salableQty;
89+
$displayQty = $this->getDisplayQty($backOrderQty, $salableQty, $requestedQty);
90+
91+
if ($displayQty > 0) {
7892
$errors = [
7993
$this->productSalabilityErrorFactory->create([
8094
'code' => 'back_order-not-enough',
8195
'message' => __(
8296
'We don\'t have as many quantity as you requested, '
8397
. 'but we\'ll back order the remaining %1.',
84-
$backOrderQty * 1
98+
$displayQty * 1
8599
)])
86100
];
87101
return $this->productSalableResultFactory->create(['errors' => $errors]);
@@ -90,4 +104,23 @@ public function execute(string $sku, int $stockId, float $requestedQty): Product
90104

91105
return $this->productSalableResultFactory->create(['errors' => []]);
92106
}
107+
108+
/**
109+
* Get display quantity to show the number of quantity customer can backorder
110+
*
111+
* @param float $backOrderQty
112+
* @param float $salableQty
113+
* @param float $requestedQty
114+
* @return float
115+
*/
116+
private function getDisplayQty(float $backOrderQty, float $salableQty, float $requestedQty): float
117+
{
118+
$displayQty = 0;
119+
if ($backOrderQty > 0 && $salableQty > 0) {
120+
$displayQty = $backOrderQty;
121+
} elseif ($requestedQty > $salableQty) {
122+
$displayQty = $requestedQty;
123+
}
124+
return $displayQty;
125+
}
93126
}

InventorySales/Test/Unit/Model/IsProductSalableCondition/BackOrderNotifyCustomerConditionTest.php

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
namespace Magento\InventorySales\Test\Unit\Model\IsProductSalableCondition;
99

10+
use Magento\Framework\Exception\LocalizedException;
1011
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
1112
use Magento\InventoryConfigurationApi\Api\Data\StockItemConfigurationInterface;
1213
use Magento\InventoryConfigurationApi\Api\GetStockItemConfigurationInterface;
@@ -15,6 +16,7 @@
1516
use Magento\InventorySalesApi\Api\Data\ProductSalabilityErrorInterfaceFactory;
1617
use Magento\InventorySalesApi\Api\Data\ProductSalableResultInterface;
1718
use Magento\InventorySalesApi\Api\Data\ProductSalableResultInterfaceFactory;
19+
use Magento\InventorySalesApi\Api\GetProductSalableQtyInterface;
1820
use Magento\InventorySalesApi\Model\GetStockItemDataInterface;
1921
use PHPUnit\Framework\MockObject\MockObject;
2022
use PHPUnit\Framework\TestCase;
@@ -28,15 +30,22 @@ class BackOrderNotifyCustomerConditionTest extends TestCase
2830
* @var BackOrderNotifyCustomerCondition
2931
*/
3032
private $model;
33+
3134
/**
3235
* @var StockItemConfigurationInterface|MockObject
3336
*/
3437
private $stockItemConfiguration;
38+
3539
/**
3640
* @var array|null
3741
*/
3842
private $stockItemData;
3943

44+
/**
45+
* @var GetProductSalableQtyInterface|MockObject
46+
*/
47+
private $getProductSalableQty;
48+
4049
/**
4150
* @inheritDoc
4251
*/
@@ -47,6 +56,9 @@ protected function setUp()
4756
$productSalableResultFactory = $this->createMock(
4857
ProductSalableResultInterfaceFactory::class
4958
);
59+
$this->getProductSalableQty = $this->createMock(
60+
GetProductSalableQtyInterface::class
61+
);
5062
$productSalableResultFactory->method('create')
5163
->willReturnCallback(
5264
function ($args) {
@@ -94,6 +106,7 @@ function () {
94106
'getStockItemData' => $getStockItemData,
95107
'productSalableResultFactory' => $productSalableResultFactory,
96108
'productSalabilityErrorFactory' => $productSalabilityErrorFactory,
109+
'getProductSalableQty' => $this->getProductSalableQty
97110
]
98111
);
99112
}
@@ -104,17 +117,26 @@ function () {
104117
* @dataProvider executeDataProvider
105118
* @param array|null $stockData
106119
* @param int $reqQty
120+
* @param int $salableQty
107121
* @param int $backOrders
108122
* @param bool $manageStock
109123
* @param array $errors
110-
* @throws \Magento\Framework\Exception\LocalizedException
124+
* @throws LocalizedException
111125
*/
112-
public function testExecute(?array $stockData, int $reqQty, int $backOrders, bool $manageStock, array $errors): void
113-
{
126+
public function testExecute(
127+
?array $stockData,
128+
int $reqQty,
129+
int $salableQty,
130+
int $backOrders,
131+
bool $manageStock,
132+
array $errors
133+
): void {
114134
$this->stockItemConfiguration->method('isManageStock')
115135
->willReturn($manageStock);
116136
$this->stockItemConfiguration->method('getBackorders')
117137
->willReturn($backOrders);
138+
$this->getProductSalableQty->method('execute')
139+
->willReturn($salableQty);
118140
$this->stockItemData = $stockData;
119141
$actualErrors = [];
120142
foreach ($this->model->execute('simple', 1, $reqQty)->getErrors() as $error) {
@@ -125,14 +147,16 @@ public function testExecute(?array $stockData, int $reqQty, int $backOrders, boo
125147

126148
/**
127149
* @return array
150+
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
128151
*/
129152
public function executeDataProvider(): array
130153
{
131154
return [
132-
'StockQty=0, ReqQty=1, Backorders=YesNotify, ManageStock=Yes' => [
155+
'StockQty=1, ReqQty=2, SalableQty=1, Backorders=YesNotify, ManageStock=Yes' => [
133156
[
134-
GetStockItemDataInterface::QUANTITY => 0,
157+
GetStockItemDataInterface::QUANTITY => 1,
135158
],
159+
2,
136160
1,
137161
StockItemConfigurationInterface::BACKORDERS_YES_NOTIFY,
138162
true,
@@ -144,45 +168,82 @@ public function executeDataProvider(): array
144168
]
145169
],
146170
],
147-
'StockQty=1, ReqQty=1, Backorders=YesNotify, ManageStock=Yes' => [
171+
'StockQty=10, ReqQty=20, SalableQty=0, Backorders=YesNotify, ManageStock=Yes' => [
172+
[
173+
GetStockItemDataInterface::QUANTITY => 10,
174+
],
175+
20,
176+
0,
177+
StockItemConfigurationInterface::BACKORDERS_YES_NOTIFY,
178+
true,
179+
[
180+
[
181+
'code' => 'back_order-not-enough',
182+
'message' => 'We don\'t have as many quantity as you requested,'
183+
. ' but we\'ll back order the remaining 20.'
184+
]
185+
],
186+
],
187+
'StockQty=10, ReqQty=20, SalableQty=15, Backorders=YesNotify, ManageStock=Yes' => [
188+
[
189+
GetStockItemDataInterface::QUANTITY => 10,
190+
],
191+
20,
192+
15,
193+
StockItemConfigurationInterface::BACKORDERS_YES_NOTIFY,
194+
true,
195+
[
196+
[
197+
'code' => 'back_order-not-enough',
198+
'message' => 'We don\'t have as many quantity as you requested,'
199+
. ' but we\'ll back order the remaining 5.'
200+
]
201+
],
202+
],
203+
'StockQty=1, ReqQty=1, SalableQty=1, Backorders=YesNotify, ManageStock=Yes' => [
148204
[
149205
GetStockItemDataInterface::QUANTITY => 1,
150206
],
151207
1,
208+
1,
152209
StockItemConfigurationInterface::BACKORDERS_YES_NOTIFY,
153210
true,
154211
[],
155212
],
156-
'StockQty=?, ReqQty=1, Backorders=YesNotify, ManageStock=Yes' => [
213+
'StockQty=?, ReqQty=1, SalableQty=1, Backorders=YesNotify, ManageStock=Yes' => [
157214
null,
158215
1,
216+
1,
159217
StockItemConfigurationInterface::BACKORDERS_YES_NOTIFY,
160218
true,
161219
[],
162220
],
163-
'StockQty=0, ReqQty=1, Backorders=No, ManageStock=Yes' => [
221+
'StockQty=0, ReqQty=1, SalableQty=1, Backorders=No, ManageStock=Yes' => [
164222
[
165223
GetStockItemDataInterface::QUANTITY => 0,
166224
],
167225
1,
226+
1,
168227
StockItemConfigurationInterface::BACKORDERS_NO,
169228
true,
170229
[],
171230
],
172-
'StockQty=0, ReqQty=1, Backorders=Yes, ManageStock=Yes' => [
231+
'StockQty=0, ReqQty=1, SalableQty=1, Backorders=Yes, ManageStock=Yes' => [
173232
[
174233
GetStockItemDataInterface::QUANTITY => 0,
175234
],
176235
1,
236+
1,
177237
StockItemConfigurationInterface::BACKORDERS_YES_NONOTIFY,
178238
true,
179239
[],
180240
],
181-
'StockQty=0, ReqQty=1, Backorders=YesNotify, ManageStock=No' => [
241+
'StockQty=0, ReqQty=1, SalableQty=1, Backorders=YesNotify, ManageStock=No' => [
182242
[
183243
GetStockItemDataInterface::QUANTITY => 0,
184244
],
185245
1,
246+
1,
186247
StockItemConfigurationInterface::BACKORDERS_YES_NOTIFY,
187248
false,
188249
[],

0 commit comments

Comments
 (0)