-
Notifications
You must be signed in to change notification settings - Fork 254
Introduce Stock quantity calculation #94
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
46c9efa
f37b38e
5c20080
a82fe04
c6a642d
2db99e4
8f2b014
2ecd5bc
eeb4545
7ae88aa
89209cc
284738f
555110a
13a74ee
328d920
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
<?php | ||
/** | ||
* Copyright © Magento, Inc. All rights reserved. | ||
* See COPYING.txt for license details. | ||
*/ | ||
namespace Magento\Inventory\Model\Stock\Command; | ||
|
||
|
||
use Magento\Framework\App\ResourceConnection; | ||
use Magento\Inventory\Indexer\Alias; | ||
use Magento\Inventory\Indexer\IndexNameBuilder; | ||
use Magento\Inventory\Indexer\IndexNameResolverInterface; | ||
use Magento\Inventory\Indexer\StockItem\IndexStructure as StockItemIndex; | ||
use Magento\Inventory\Indexer\StockItemIndexerInterface; | ||
use Magento\Inventory\Setup\Operation\CreateReservationTable; | ||
use Magento\InventoryApi\Api\Data\ReservationInterface; | ||
use Magento\InventoryApi\Api\GetProductQuantityInStockInterface; | ||
|
||
/** | ||
* Return Quantity of products available to be sold by Product SKU and Stock Id | ||
* | ||
* @see \Magento\InventoryApi\Api\GetProductQuantityInStockInterface | ||
* @api | ||
*/ | ||
class GetProductQuantityInStock implements GetProductQuantityInStockInterface | ||
{ | ||
/** | ||
* @var ResourceConnection | ||
*/ | ||
private $resource; | ||
|
||
/** | ||
* @var IndexNameBuilder | ||
*/ | ||
private $indexNameBuilder; | ||
|
||
/** | ||
* @var IndexNameResolverInterface | ||
*/ | ||
private $indexNameResolver; | ||
|
||
/** | ||
* GetProductQuantityInStock constructor. | ||
* | ||
* @param ResourceConnection $resource | ||
* @param IndexNameBuilder $indexNameBuilder | ||
* @param IndexNameResolverInterface $indexNameResolver | ||
*/ | ||
public function __construct( | ||
ResourceConnection $resource, | ||
IndexNameBuilder $indexNameBuilder, | ||
IndexNameResolverInterface $indexNameResolver | ||
) { | ||
$this->resource = $resource; | ||
$this->indexNameBuilder = $indexNameBuilder; | ||
$this->indexNameResolver = $indexNameResolver; | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
public function execute(string $sku, int $stockId): float | ||
{ | ||
$productQtyInStock = $this->getStockItemQty($sku, $stockId) - $this->getReservationQty($sku, $stockId); | ||
return (float) $productQtyInStock; | ||
} | ||
|
||
|
||
/** | ||
* Return product quantity in stock. | ||
* | ||
* @param string $sku | ||
* @param int $stockId | ||
* @return float | ||
*/ | ||
private function getStockItemQty(string $sku, int $stockId): float | ||
{ | ||
$indexName = $this->indexNameBuilder | ||
->setIndexId(StockItemIndexerInterface::INDEXER_ID) | ||
->addDimension('stock_', $stockId) | ||
->setAlias(Alias::ALIAS_MAIN) | ||
->create(); | ||
|
||
$stockItemTableName = $this->indexNameResolver->resolveName($indexName); | ||
|
||
$connection = $this->resource->getConnection(); | ||
|
||
$select = $connection->select() | ||
->from($stockItemTableName, [StockItemIndex::QUANTITY]) | ||
->where(StockItemIndex::SKU . '=?', $sku) | ||
->limit(1); | ||
|
||
$stockItemQty = $connection->fetchOne($select); | ||
if (false === $stockItemQty) { | ||
$stockItemQty = 0; | ||
} | ||
|
||
return (float) $stockItemQty; | ||
} | ||
|
||
/** | ||
* Return the sum of all product reservations in stock. | ||
* | ||
* @param string $sku | ||
* @param int $stockId | ||
* @return float | ||
*/ | ||
private function getReservationQty(string $sku, int $stockId): float | ||
{ | ||
$connection = $this->resource->getConnection(); | ||
|
||
$reservationTableName = $connection->getTableName(CreateReservationTable::TABLE_NAME_RESERVATION); | ||
|
||
$select = $connection->select() | ||
->from($reservationTableName, [ReservationInterface::QUANTITY => 'sum(' . ReservationInterface::QUANTITY . ')']) | ||
->where(ReservationInterface::SKU . '=?', $sku) | ||
->where(ReservationInterface::STOCK_ID . '=?', $stockId) | ||
->limit(1); | ||
|
||
$reservationQty = $connection->fetchOne($select); | ||
if (false === $reservationQty) { | ||
$reservationQty = 0; | ||
} | ||
|
||
return (float) $reservationQty; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<?php | ||
/** | ||
* Copyright © Magento, Inc. All rights reserved. | ||
* See COPYING.txt for license details. | ||
*/ | ||
namespace Magento\InventoryApi\Api; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, the namespace is correct. |
||
|
||
/** | ||
* Service which returns Quantity of products available to be sold by Product SKU and Stock Id | ||
* | ||
* @api | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Btw, we need to add this service to Web API webapi.xml in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, wondering if I can use the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have open task for refactoring of our ACL rules But in this case need to discuss maybe need to relate on some product resource |
||
*/ | ||
interface GetProductQuantityInStockInterface | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It's a good question But, having "Stock" entity introduced by MSI. Currently, this service accepts Based on the above IMO current naming - There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree, here we have a term "stock" that violates the "ubiquitous language" DDD principle because in the same context it can mean both the "available quantity" (more high-level) and the "virtual aggregation" (more low-level); probably we should think about it; what's more, as I introduced during our last weekly meeting, in the future, when we will have the sales channel connected to our stock (virtual aggregation), maybe this service could be better named There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @aleron75 you made a very true statement regarding violation of "ubiquitous language". Regarding So, semantically it's incorrect to say - give me product quantity for current Sales Channel providing $stockId as the parameter. Does it make sense? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
you're perfectly right, it should be instead: "give me product quantity for current Sales Channel providing some channel id as the parameter" where the by the way at the moment let's leave the service as is and just postpone this discussion when we'll work on the channel entity; thank you There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @aleron75 100% agree, let's see how our interfaces will evolve and later align naming accordingly. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My opinion is to follow YAGNI principle. So we should not create service related to Sales Channels. We will do it when we will work on this task. |
||
{ | ||
/** | ||
* Get Product Quantity for given SKU in a given Stock | ||
* | ||
* @param string $sku | ||
* @param int $stockId | ||
* @return float | ||
*/ | ||
public function execute(string $sku, int $stockId): float; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you add this string? By default reservation id is null
But maybe I do not take into account some cause
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason is that If we don't pass the reservation ID, the constructor (used by the object manager) will throw an exception because it's a constructor mandatory parameter