diff --git a/Controller/Product/Index.php b/Controller/Product/Index.php index c3bbb2b..9018916 100644 --- a/Controller/Product/Index.php +++ b/Controller/Product/Index.php @@ -151,7 +151,10 @@ protected function prepareCollection() //Filter on is_saleable if defined if ($this->scopeConfig->getValue(Config::XML_PATH_PRODUCT_SYNCHRONIZATION_SALABLE_ONLY)) { - $collection->addFieldToFilter('is_saleable', true); + $collection->getSelect()->where( + 'stock_status_index.stock_status = ?', + \Magento\CatalogInventory\Model\Stock\Status::STATUS_IN_STOCK + ); } $visibility = $this->scopeConfig->getValue(Config::XML_PATH_PRODUCT_SYNCHRONIZATION_VISIBILITY); diff --git a/Model/Api.php b/Model/Api.php index 7258843..16937a8 100644 --- a/Model/Api.php +++ b/Model/Api.php @@ -64,10 +64,15 @@ public function addProduct($params) public function removeProduct($productId) { $params = [ - 'products' => $productId, + 'products' => [$productId], ]; - $this->get('product/remove', $params); + // work around a problem that API fails with query like ?products[0]=123&products[1]=456 + // make it like this: ?products[]=123&products[]=456 + $query = http_build_query($params); + $query = preg_replace('/%5B[0-9]+%5D/simU', '%5B%5D', $query); + + $this->get('product/remove?' . $query); } /** diff --git a/Observer/ProductSaveEntityAfterObserver.php b/Observer/ProductSaveEntityAfterObserver.php index 377c2e5..66508c6 100644 --- a/Observer/ProductSaveEntityAfterObserver.php +++ b/Observer/ProductSaveEntityAfterObserver.php @@ -5,6 +5,8 @@ use Clerk\Clerk\Model\Api; use Clerk\Clerk\Model\Config; use Magento\Catalog\Model\Product; +use Magento\CatalogInventory\Helper\Stock; +use Magento\CatalogInventory\Model\StockRegistryStorage; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Event\ManagerInterface; use Magento\Framework\Event\Observer; @@ -23,6 +25,16 @@ class ProductSaveEntityAfterObserver implements ObserverInterface */ protected $scopeConfig; + /** + * @var Stock + */ + protected $stockHelper; + + /** + * @var StockRegistryStorage + */ + protected $stockRegistryStorage; + /** * @var ManagerInterface */ @@ -36,12 +48,16 @@ class ProductSaveEntityAfterObserver implements ObserverInterface public function __construct( ObjectManagerInterface $objectManager, ScopeConfigInterface $scopeConfig, + Stock $stockHelper, + StockRegistryStorage $stockRegistryStorage, ManagerInterface $eventManager, Api $api ) { $this->objectManager = $objectManager; $this->scopeConfig = $scopeConfig; + $this->stockHelper = $stockHelper; + $this->stockRegistryStorage = $stockRegistryStorage; $this->eventManager = $eventManager; $this->api = $api; } @@ -62,12 +78,14 @@ public function execute(\Magento\Framework\Event\Observer $observer) //Cancel if product visibility is not as defined if ($product->getVisibility() != $this->scopeConfig->getValue(Config::XML_PATH_PRODUCT_SYNCHRONIZATION_VISIBILITY)) { + $this->api->removeProduct($product->getId()); return; } //Cancel if product is not saleable if ($this->scopeConfig->getValue(Config::XML_PATH_PRODUCT_SYNCHRONIZATION_SALABLE_ONLY)) { - if (!$product->isSalable()) { + if (!$this->isSalable($product)) { + $this->api->removeProduct($product->getId()); return; } } @@ -112,4 +130,24 @@ public function execute(\Magento\Framework\Event\Observer $observer) } } } -} \ No newline at end of file + + /** + * Checks if product is salable + * + * Works around problems with cached + * + * @param Product $product + * @return bool + */ + public function isSalable(Product $product) + { + $productId = $product->getId(); + + // isSalable relies on status that is assigned after initial product load + // stock registry holds cached old stock status, invalidate to force reload + $this->stockRegistryStorage->removeStockStatus($productId); + $this->stockHelper->assignStatusToProduct($product); + + return $product->isSalable(); + } +}