Skip to content

Commit 39cd899

Browse files
authored
Merge pull request #8 from magento-troll/MDEE-40-real-qty
MDEE-40:[Commerce Export] Implement stock_item_status feed
2 parents 920c4e9 + 54c9a1a commit 39cd899

File tree

8 files changed

+209
-41
lines changed

8 files changed

+209
-41
lines changed

DataExporter/Export/Extractor.php

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,19 +48,11 @@ private function indexDataByArguments(array $field, array $data, bool $isRoot) :
4848
}
4949
if ($field['repeated'] && !$isRoot) {
5050
foreach ($data as $item) {
51-
$index = [];
52-
foreach ($field['using'] as $key) {
53-
$index[] = [$key['field'] => $item[$key['field']]];
54-
}
55-
$output[base64_encode(json_encode($index))][] = $item[$field['name']];
51+
$output[LookupBuilder::build($field, $item)][] = $item[$field['name']];
5652
}
5753
} else {
5854
foreach ($data as $item) {
59-
$index = [];
60-
foreach ($field['using'] as $key) {
61-
$index[] = [$key['field'] => $item[$key['field']]];
62-
}
63-
$output[base64_encode(json_encode($index))] = $item[$field['name']];
55+
$output[LookupBuilder::build($field, $item)] = $item[$field['name']];
6456
}
6557
}
6658
return $output;

DataExporter/Export/LookupBuilder.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\DataExporter\Export;
9+
10+
/**
11+
* Build lookup value based in "using" field
12+
*/
13+
class LookupBuilder
14+
{
15+
16+
/**
17+
* @param array $field
18+
* @param array $item
19+
* @return string
20+
*/
21+
public static function build(array $field, array $item): string
22+
{
23+
$index = [];
24+
if (isset($field['using'])) {
25+
foreach ($field['using'] as $key) {
26+
if (!isset($key['field'], $item[$key['field']])) {
27+
$fieldName = $key['field'] ?? '';
28+
throw new \InvalidArgumentException(\sprintf(
29+
'DataExporter error: No value in Data Provider for "%s" specified in "using" expression: "%s"',
30+
$fieldName,
31+
\var_export($field, true)
32+
));
33+
}
34+
// cast to string: we don't care about type here
35+
$index[] = [$key['field'] => (string)$item[$key['field']]];
36+
}
37+
}
38+
return base64_encode(json_encode($index));
39+
}
40+
}

DataExporter/Export/Transformer.php

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -173,23 +173,30 @@ private function convertComplexRow(array $row, array $type, array $snapshot) : a
173173
if ($field['provider'] != null) {
174174
$key = $this->getKey($field);
175175
if (isset($snapshot[$key])) {
176-
$index = [];
177-
foreach ($field['using'] as $key) {
178-
$index[] = [$key['field'] => $row[$key['field']]];
179-
}
180-
$lookupReference = \base64_encode(\json_encode($index));
176+
$lookupReference = LookupBuilder::build($field, $row);
181177
//todo: add Filter cond
182-
$result[$field['name']] = $this->convertComplexData($field, $snapshot, $lookupReference);
178+
$valueFromProvider = $this->convertComplexData($field, $snapshot, $lookupReference);
179+
$result[$field['name']] = $valueFromProvider ?? $this->getDefaultValueFromParent($field, $row);
183180
}
184181
} else {
185-
if (isset($row[$field['name']])) {
186-
$result[$field['name']] = $this->castToFieldType($field, $row[$field['name']]);
187-
}
182+
$result[$field['name']] = $this->getDefaultValueFromParent($field, $row);
188183
}
189184
}
190185
return $result;
191186
}
192187

188+
/**
189+
* @param array $field
190+
* @param array $row
191+
* @return array|bool|float|int|string|null
192+
*/
193+
private function getDefaultValueFromParent(array $field, array $row) {
194+
if (isset($row[$field['name']])) {
195+
return $this->castToFieldType($field, $row[$field['name']]);
196+
}
197+
return null;
198+
}
199+
193200
/**
194201
* Convert complex data
195202
*
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\InventoryDataExporter\Model\Provider;
10+
11+
use Magento\QueryXml\Model\QueryProcessor;
12+
13+
/**
14+
* Get qty for sale. Calculated as "<qty from index> + SUM(<reservations>)"
15+
*/
16+
class QtyForSale
17+
{
18+
/**
19+
* @var QueryProcessor
20+
*/
21+
private $queryProcessor;
22+
23+
/**
24+
* @var string
25+
*/
26+
private $queryName;
27+
28+
/**
29+
* @var string[]
30+
*/
31+
private $data;
32+
33+
/**
34+
* @param QueryProcessor $queryProcessor
35+
* @param string $queryName
36+
* @param string[] $data
37+
*/
38+
public function __construct(
39+
QueryProcessor $queryProcessor,
40+
string $queryName = 'inventoryExporterGetReservations',
41+
array $data = []
42+
) {
43+
$this->data = $data;
44+
$this->queryName = $queryName;
45+
$this->queryProcessor = $queryProcessor;
46+
}
47+
48+
/**
49+
* Getting inventory stock statuses.
50+
*
51+
* @param array $values
52+
* @return array
53+
* @throws \Zend_Db_Statement_Exception
54+
*/
55+
public function get(array $values): array
56+
{
57+
$queryArguments = $this->data;
58+
$queryArguments['skus'] = [];
59+
$queryArguments['stock_ids'] = [];
60+
$skuPerStockQty = [];
61+
$output = [];
62+
63+
foreach ($values as $value) {
64+
$sku = $value['sku'];
65+
$stockId = $value['stockId'];
66+
$queryArguments['skus'][] = $sku;
67+
$queryArguments['stock_ids'][] = $stockId;
68+
$skuPerStockQty[StockStatusIdBuilder::build($value)] = $value['qty'];
69+
}
70+
$cursor = $this->queryProcessor->execute($this->queryName, $queryArguments);
71+
while ($row = $cursor->fetch()) {
72+
$uniqueKey = StockStatusIdBuilder::build($row);
73+
if (isset($skuPerStockQty[$uniqueKey])) {
74+
// TODO: if infinitive set to "0", check for "<0"
75+
$output[] = [
76+
'sku' => $row['sku'],
77+
'stockId' => $row['stockId'],
78+
'qtyForSale' => $skuPerStockQty[$uniqueKey] + $row['quantity']
79+
];
80+
}
81+
}
82+
return $output;
83+
}
84+
}

InventoryDataExporter/Model/Provider/StockStatus.php

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -64,46 +64,38 @@ public function get(array $values): array
6464

6565
$connection = $this->resourceConnection->getConnection();
6666
$output = [];
67+
//TODO: limit stock-source ids
68+
6769
foreach ($skuListInStock as $skuInStock) {
6870
$select = $this->query->getQuery($skuInStock->getSkuList(), $skuInStock->getStockId());
6971
try {
7072
$cursor = $connection->query($select);
7173
while ($row = $cursor->fetch()) {
7274
$row['stockId'] = $skuInStock->getStockId();
73-
$row['id'] = $this->buildStockStatusId($row);
75+
$row['id'] = StockStatusIdBuilder::build($row);
76+
77+
// set default values
78+
$row['infiniteStock'] = false;
79+
$row['qtyForSale'] = $row['qty'];
7480
$output[] = $row;
7581
}
7682
} catch (\Throwable $e) {
7783
// handle case when view "inventory_stock_1" for default Stock does not exists
78-
$output += \array_map(function ($sku) use ($skuInStock){
84+
$output += \array_map(static function ($sku) use ($skuInStock){
7985
$row = [
8086
'qty' => 0,
8187
'isSalable' => false,
8288
'sku' => $sku,
8389
'stockId' => $skuInStock->getStockId(),
90+
'infiniteStock' => false,
91+
'qtyForSale' => 0
8492
];
85-
$row['id'] = $this->buildStockStatusId($row);
93+
$row['id'] = StockStatusIdBuilder::build($row);
8694
return $row;
8795
}, $skuInStock->getSkuList());
8896
}
8997
}
90-
return $output;
91-
}
9298

93-
/**
94-
* @param array $row
95-
* @return string
96-
*/
97-
private function buildStockStatusId(array $row): string
98-
{
99-
if (!isset($row['stockId'], $row['sku'])) {
100-
throw new \RuntimeException(
101-
sprintf(
102-
"inventory_data_exporter_stock_status indexer error: cannot build unique id from %s",
103-
\implode(", ", $row)
104-
)
105-
);
106-
}
107-
return \hash('md5', $row['stockId'] . "\0" . $row['sku']);
99+
return $output;
108100
}
109101
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\InventoryDataExporter\Model\Provider;
10+
11+
/**
12+
* Build ID based on stockId & sku
13+
*/
14+
class StockStatusIdBuilder
15+
{
16+
/**
17+
* @param array $row
18+
* @return string
19+
*/
20+
public static function build(array $row): string
21+
{
22+
if (!isset($row['stockId'], $row['sku'])) {
23+
throw new \RuntimeException(
24+
sprintf(
25+
"inventory_data_exporter_stock_status indexer error: cannot build unique id from %s",
26+
\var_export($row, true)
27+
)
28+
);
29+
}
30+
return \hash('md5', $row['stockId'] . "\0" . $row['sku']);
31+
}
32+
}

InventoryDataExporter/etc/et_schema.xml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@
1414
<field name="stockId" type="ID"/>
1515
<field name="sku" type="String"/>
1616
<field name="qty" type="Float" />
17-
<field name="qtyForSale" type="Float"/>
17+
<field name="qtyForSale" type="Float" provider="Magento\InventoryDataExporter\Model\Provider\QtyForSale">
18+
<using field="sku" />
19+
<using field="stockId" />
20+
</field>
1821
<field name="infiniteStock" type="Boolean" provider="Magento\InventoryDataExporter\Model\Provider\InfiniteStock">
1922
<using field="sku" />
2023
</field>

InventoryDataExporter/etc/query.xml

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,22 @@
7070
</link-source>
7171
</source>
7272
</query>
73-
</config>
73+
74+
<!-- Calculate reserved qty. Used to calculate real quantity
75+
76+
SELECT SUM(quantity) AS `quantity`, sku FROM `inventory_reservation`
77+
WHERE stock_id in (<stock_ids>) AND sku in (<skus>)
78+
GROUP by stock_id, sku
79+
-->
80+
<query name="inventoryExporterGetReservations">
81+
<source name="inventory_reservation">
82+
<attribute name="quantity" alias="quantity" function="sum"/>
83+
<filter glue="and">
84+
<condition attribute="sku" operator="in" type="placeholder">skus</condition>
85+
<condition attribute="stock_id" operator="in" type="placeholder">stock_ids</condition>
86+
</filter>
87+
<attribute name="stock_id" group="true" alias="stockId" />
88+
<attribute name="sku" group="true" />
89+
</source>
90+
</query>
91+
</config>

0 commit comments

Comments
 (0)