Skip to content

Commit

Permalink
#518 Refactored the default shipping selection algo to process source…
Browse files Browse the repository at this point in the history
…s in priority order
  • Loading branch information
Roman Glushko committed Feb 17, 2018
1 parent 0b22472 commit 68694a8
Showing 1 changed file with 99 additions and 62 deletions.
161 changes: 99 additions & 62 deletions app/code/Magento/InventoryShipping/Model/DefaultShippingAlgorithm.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,32 @@

namespace Magento\InventoryShipping\Model;

use Magento\Inventory\Model\SourceItem\Command\GetSourceItemsBySkuInterface;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Inventory\Model\SourceItem\Command\GetSourceItemsBySkuInterfffface;
use Magento\InventoryApi\Api\Data\SourceInterface;
use Magento\InventoryApi\Api\Data\SourceItemInterface;
use Magento\InventoryApi\Api\Data\SourceItemInterfaceFactory;
use Magento\InventoryApi\Api\GetAssignedSourcesForStockInterface;
use Magento\InventoryApi\Api\SourceRepositoryInterface;
use Magento\InventoryApi\Api\SourceItemRepositoryInterface;
use Magento\InventorySalesApi\Api\Data\SalesChannelInterface;
use Magento\InventorySalesApi\Api\StockResolverInterface;
use Magento\InventoryShipping\Model\ShippingAlgorithmResult\ShippingAlgorithmResultInterface;
use Magento\InventoryShipping\Model\ShippingAlgorithmResult\ShippingAlgorithmResultInterfaceFactory;
use Magento\InventoryShipping\Model\ShippingAlgorithmResult\SourceItemSelectionInterfaceFactory;
use Magento\InventoryShipping\Model\ShippingAlgorithmResult\SourceSelectionInterfaceFactory;
use Magento\Sales\Api\Data\OrderInterface;
use Magento\Sales\Api\Data\OrderItemInterface;
use Magento\Sales\Model\Order\Item as OrderItem;
use Magento\Store\Api\WebsiteRepositoryInterface;
use Magento\Store\Model\StoreManagerInterface;

/**
* {@inheritdoc}
*
* This shipping algorithm just iterates over all the sources one by one in no particular order
* This shipping algorithm just iterates over all the sources one by one in particular order
*/
class DefaultShippingAlgorithm implements ShippingAlgorithmInterface
{
/**
* @var GetSourceItemsBySkuInterface
*/
private $getSourceItemsBySku;

/**
* @var SourceSelectionInterfaceFactory
*/
Expand All @@ -48,11 +48,6 @@ class DefaultShippingAlgorithm implements ShippingAlgorithmInterface
*/
private $shippingAlgorithmResultFactory;

/**
* @var bool
*/
private $isShippable;

/**
* @var StoreManagerInterface
*/
Expand All @@ -74,114 +69,156 @@ class DefaultShippingAlgorithm implements ShippingAlgorithmInterface
private $getAssignedSourcesForStock;

/**
* @param GetSourceItemsBySkuInterface $getSourceItemsBySku
* @var SourceItemRepositoryInterface
*/
private $sourceItemRepository;

/**
* @var SearchCriteriaBuilder
*/
private $searchCriteriaBuilder;
/**
* @var SourceItemInterfaceFactory
*/
private $sourceItemFactory;

/**
* @param SourceSelectionInterfaceFactory $sourceSelectionFactory
* @param SourceItemSelectionInterfaceFactory $sourceItemSelectionFactory
* @param ShippingAlgorithmResultInterfaceFactory $shippingAlgorithmResultFactory
* @param StoreManagerInterface $storeManager
* @param WebsiteRepositoryInterface $websiteRepository
* @param StockResolverInterface $stockResolver
* @param GetAssignedSourcesForStockInterface $getAssignedSourcesForStock
* @param SourceItemRepositoryInterface $sourceItemRepository
* @param SourceItemInterfaceFactory $sourceItemFactory
* @param SearchCriteriaBuilder $searchCriteriaBuilder
*/
public function __construct(
GetSourceItemsBySkuInterface $getSourceItemsBySku,
SourceSelectionInterfaceFactory $sourceSelectionFactory,
SourceItemSelectionInterfaceFactory $sourceItemSelectionFactory,
ShippingAlgorithmResultInterfaceFactory $shippingAlgorithmResultFactory,
StoreManagerInterface $storeManager,
WebsiteRepositoryInterface $websiteRepository,
StockResolverInterface $stockResolver,
GetAssignedSourcesForStockInterface $getAssignedSourcesForStock
GetAssignedSourcesForStockInterface $getAssignedSourcesForStock,
SourceItemRepositoryInterface $sourceItemRepository,
SourceItemInterfaceFactory $sourceItemFactory,
SearchCriteriaBuilder $searchCriteriaBuilder
) {
$this->getSourceItemsBySku = $getSourceItemsBySku;
$this->shippingAlgorithmResultFactory = $shippingAlgorithmResultFactory;
$this->sourceSelectionFactory = $sourceSelectionFactory;
$this->sourceItemSelectionFactory = $sourceItemSelectionFactory;
$this->storeManager = $storeManager;
$this->websiteRepository = $websiteRepository;
$this->stockResolver = $stockResolver;
$this->getAssignedSourcesForStock = $getAssignedSourcesForStock;
$this->sourceItemRepository = $sourceItemRepository;
$this->searchCriteriaBuilder = $searchCriteriaBuilder;
$this->sourceItemFactory = $sourceItemFactory;
}

/**
* @inheritdoc
*/
public function execute(OrderInterface $order): ShippingAlgorithmResultInterface
{
$this->isShippable = true;
$sourceItemSelectionsData = $this->getSourceItemSelectionsData($order);

$isShippable = true;
$sourceSelections = [];
foreach ($sourceItemSelectionsData as $sourceCode => $sourceItemSelections) {
$sourceSelections[] = $this->sourceSelectionFactory->create([
'sourceCode' => $sourceCode,
'sourceItemSelections' => $sourceItemSelections,
]);
}

$shippingResult = $this->shippingAlgorithmResultFactory->create([
'sourceSelections' => $sourceSelections,
'isShippable' => $this->isShippable
]);
return $shippingResult;
}

/**
* Key is source code, value is list of SourceItemSelectionInterface related to this source
* @param OrderInterface $order
* @return array
*/
private function getSourceItemSelectionsData(OrderInterface $order): array
{
$sourceItemSelections = [];
$storeId = $order->getStoreId();
$sources = $this->getSourcesByStoreId($storeId);

/** @var OrderItemInterface|OrderItem $orderItem */
foreach ($order->getItems() as $orderItem) {
if ($orderItem->isDeleted() || $orderItem->getParentItemId()) {
$qtyToDeliver = $orderItem->getQtyOrdered();

if ($orderItem->isDeleted() || $orderItem->getParentItemId() || $this->isZero($qtyToDeliver)) {
continue;
}

$itemSku = $orderItem->getSku();
$sourceItems = $this->getSourceItemsBySku->execute($orderItem->getSku());
foreach ($sources as $source) {
$sourceItem = $this->getStockItemBySku($source->getSourceCode(), $itemSku);
$sourceItemQty = $sourceItem->getQuantity();

$qtyToDeliver = $orderItem->getQtyOrdered();
foreach ($sourceItems as $sourceItem) {
if ($qtyToDeliver < 0.0001) {
break;
if ($this->isZero($sourceItemQty)) {
continue;
}

$sourceItemQty = $sourceItem->getQuantity();
if ($sourceItemQty > 0) {
$qtyToDeduct = min($sourceItemQty, $qtyToDeliver);
$qtyToDeduct = min($sourceItemQty, $qtyToDeliver);

$sourceItemSelections[$sourceItem->getSourceCode()][] = $this->sourceItemSelectionFactory->create([
$sourceSelections[] = $this->sourceSelectionFactory->create([
'sourceCode' => $sourceItem->getSourceCode(),
'sourceItemSelections' => $this->sourceItemSelectionFactory->create([
'sku' => $itemSku,
'qty' => $qtyToDeduct,
'qtyAvailable' => $sourceItemQty,
]);
]),
]);

$qtyToDeliver -= $qtyToDeduct;
}
$qtyToDeliver -= $qtyToDeduct;
}

if ($qtyToDeliver > 0.0001) {
$this->isShippable = false;
if (!$this->isZero($qtyToDeliver)) {
$isShippable = false;
}
}

return $sourceItemSelections;

return $this->shippingAlgorithmResultFactory->create([
'sourceSelections' => $sourceSelections,
'isShippable' => $isShippable
]);
}

/**
* Retrieve sources are related to current stock that are ordered by priority
*
* @param int $storeId
*
* @return SourceItemInterface[]
* @return SourceInterface[]
*/
private function getSourcesByStoreId($storeId)
private function getSourcesByStoreId(int $storeId): array
{
$store = $this->storeManager->getStore($storeId);
$webstore = $this->websiteRepository->getById($store->getId());
$stock = $this->stockResolver->get(SalesChannelInterface::TYPE_WEBSITE, $webstore->getCode());
$website = $this->websiteRepository->getById($store->getId());
$stock = $this->stockResolver->get(SalesChannelInterface::TYPE_WEBSITE, $website->getCode());

return $this->getAssignedSourcesForStock->execute($stock->getStockId());
}

/**
* Retrieve stock item from specific source by SKU
*
* @param string $stockCode
* @param string $sku
*
* @return SourceItemInterface
*/
private function getStockItemBySku(string $stockCode, string $sku): SourceItemInterface
{
$searchCriteria = $this->searchCriteriaBuilder
->addFilter(SourceItemInterface::SOURCE_CODE, $stockCode)
->addFilter(SourceItemInterface::SKU, $sku)
->create();
$sourceItemsResult = $this->sourceItemRepository->getList($searchCriteria);

if ($sourceItemsResult->getTotalCount() > 0) {
return reset($sourceItemsResult->getItems());
}

return $this->sourceItemFactory->create();
}

/**
* Compare float number with some epsilon
*
* @param float $floatNumber
*
* @return bool
*/
private function isZero(float $floatNumber): bool
{
return $floatNumber < 0.0001;
}
}

0 comments on commit 68694a8

Please sign in to comment.