diff --git a/Model/Config/Source/Attributes.php b/Model/Config/Source/Attributes.php index 4ecb548..125e407 100755 --- a/Model/Config/Source/Attributes.php +++ b/Model/Config/Source/Attributes.php @@ -95,7 +95,7 @@ function ($a, $b) { */ public function getNonAvailableAttributes(): array { - return ['categories', 'gallery', 'category_ids', 'quantity_and_stock_status']; + return ['categories', 'gallery', 'category_ids', 'quantity_and_stock_status', 'price', 'special_price']; } /** diff --git a/Model/ProductData/Repository.php b/Model/ProductData/Repository.php index 9487a67..ba1af4d 100755 --- a/Model/ProductData/Repository.php +++ b/Model/ProductData/Repository.php @@ -80,6 +80,10 @@ class Repository implements ProductData * @var array */ private $entityIds; + /** + * @var array + */ + private $parentSimples; /** * @var Type */ @@ -134,6 +138,7 @@ public function getProductData(int $storeId = 0, ?array $entityIds = null, int $ { $this->collectIds($storeId, $entityIds); $this->collectAttributes($storeId); + $this->parentSimples = []; $this->staticFields = $this->dataConfigRepository->getStaticFields($storeId); $this->imageData = $this->image->execute($this->entityIds, $storeId); @@ -148,17 +153,54 @@ public function getProductData(int $storeId = 0, ?array $entityIds = null, int $ $result[$entityId][$index] = $this->prepareAttribute($attr, $productData); } $result[$entityId] += $this->categoryData($productData); + + if (!empty($productData['parent_id'])) { + $this->parentSimples[$productData['parent_id']][] = $productData['product_id']; + } } - if ($this->dataConfigRepository->getFilters($storeId)['exclude_out_of_stock']) { - foreach ($result as $id => &$row) { - if ($row['sqr:availability'] == 'out of stock') { - unset($result[$id]); - } + return $this->postProcess($result, $storeId); + } + + /** + * @param array $result + * @param int $storeId + * @return array + */ + private function postProcess(array $result, int $storeId = 0): array + { + if (!$this->dataConfigRepository->getFilters($storeId)['exclude_out_of_stock'] || empty($result)) { + return $result; + } + + $unsetSimples = []; + foreach ($result as $id => &$row) { + + // Remove parent products without simples + if ($row['sqr:id'] == $row['sqr:assoc_id'] && $row['sqr:price'] == 0.00) { + $unsetSimples[] = $id; + continue; + } + + // Remove out of stock products + if ($row['sqr:availability'] != 'out of stock') { + continue; + } + $unsetSimples[] = $id; + + if (!empty($row['sqr:assoc_id']) && isset($this->parentSimples[$row['sqr:assoc_id']])) { + $this->parentSimples[$row['sqr:assoc_id']] = array_diff( + $this->parentSimples[$row['sqr:assoc_id']], + [$id] + ); } } - return $result; + $emptyParents = array_keys(array_filter($this->parentSimples), function ($value) { + return empty($value); + }); + + return array_diff_key($result, array_flip($emptyParents) + array_flip($unsetSimples)); } /** @@ -354,7 +396,7 @@ private function prepareAttribute(string $attribute, array $productData) } break; case 'status': - return ($value) ? 'Enabled' : 'Disabled'; + return ($value == 1) ? 'Enabled' : 'Disabled'; case 'is_in_stock': return ($value) ? 'in stock' : 'out of stock'; case 'manage_stock': diff --git a/Service/ProductData/AttributeCollector/Data/Parents.php b/Service/ProductData/AttributeCollector/Data/Parents.php index 8ab0dd6..3a5c5d2 100755 --- a/Service/ProductData/AttributeCollector/Data/Parents.php +++ b/Service/ProductData/AttributeCollector/Data/Parents.php @@ -9,6 +9,7 @@ use Exception; use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; use Magento\Framework\App\ResourceConnection; use Magento\Framework\EntityManager\MetadataPool; @@ -50,12 +51,12 @@ public function __construct( * @param array[] $entityIds array of product IDs * @return array[] */ - public function execute($entityIds = []): array + public function execute($entityIds = [], bool $excludeDisabled = true): array { if (empty($entityIds)) { - return $this->collectAllParents(); + return $this->collectAllParents($excludeDisabled); } - return $this->collectParents($entityIds); + return $this->collectParents($entityIds, $excludeDisabled); } /** @@ -63,7 +64,7 @@ public function execute($entityIds = []): array * * @return array[] */ - private function collectAllParents(): array + private function collectAllParents(bool $excludeDisabled): array { $result = []; $select = $this->resource->getConnection() @@ -75,9 +76,24 @@ private function collectAllParents(): array "catalog_product_entity.{$this->linkField} = catalog_product_relation.parent_id", 'type_id' ); + + if ($excludeDisabled && $attributeId = $this->getStatusAttributeId()) { + $select->joinLeft( + ['catalog_product_entity_int' => $this->resource->getTableName('catalog_product_entity_int')], + "catalog_product_entity_int.entity_id = catalog_product_entity.{$this->linkField}" + )->where( + 'catalog_product_entity_int.value = ?', + Status::STATUS_ENABLED + )->where( + 'catalog_product_entity_int.attribute_id = ?', + $attributeId + ); + } + foreach ($this->resource->getConnection()->fetchAll($select) as $item) { $result[$item['child_id']][$item['parent_id']] = $item['type_id']; } + return $result; } @@ -87,7 +103,7 @@ private function collectAllParents(): array * @param array[] $entityIds array of product IDs * @return array[] */ - private function collectParents(array $entityIds): array + private function collectParents(array $entityIds, bool $excludeDisabled): array { $all = $entityIds; $result = []; @@ -100,11 +116,51 @@ private function collectParents(array $entityIds): array "catalog_product_entity.{$this->linkField} = catalog_product_relation.parent_id", 'type_id' )->where('child_id IN (?)', $entityIds); - $relations = $this->resource->getConnection()->fetchAll($select); - foreach ($relations as $item) { + + if ($excludeDisabled && $attributeId = $this->getStatusAttributeId()) { + $select->joinLeft( + ['catalog_product_entity_int' => $this->resource->getTableName('catalog_product_entity_int')], + "catalog_product_entity_int.entity_id = catalog_product_entity.{$this->linkField}" + )->where( + 'catalog_product_entity_int.value = ?', + Status::STATUS_ENABLED + )->where( + 'catalog_product_entity_int.attribute_id = ?', + $attributeId + ); + } + + foreach ($this->resource->getConnection()->fetchAll($select) as $item) { $result[$item['child_id']][$item['parent_id']] = $item['type_id']; $all += [$item['child_id'], $item['parent_id']]; } + return ['all' => array_unique($all), 'relations' => $result]; } + + /** + * Get attribute id for status attribute + * + * @return int + */ + private function getStatusAttributeId(): int + { + $connection = $this->resource->getConnection(); + $selectAttributeId = $connection->select()->from( + ['eav_attribute' => $this->resource->getTableName('eav_attribute')], + ['attribute_id'] + )->joinLeft( + ['eav_entity_type' => $this->resource->getTableName('eav_entity_type')], + 'eav_entity_type.entity_type_id = eav_attribute.entity_type_id', + [] + )->where( + 'entity_type_code = ?', + 'catalog_product' + )->where( + 'attribute_code = ?', + 'status' + ); + + return (int)$connection->fetchOne($selectAttributeId); + } } diff --git a/Service/ProductData/AttributeCollector/Data/Price.php b/Service/ProductData/AttributeCollector/Data/Price.php index c6d2efd..f9bd84e 100755 --- a/Service/ProductData/AttributeCollector/Data/Price.php +++ b/Service/ProductData/AttributeCollector/Data/Price.php @@ -192,7 +192,7 @@ private function getProductData(array $productIds = []) 'price_index.customer_group_id = 0' ] ), - ['final_price', 'min_price', 'max_price'] + ['final_price', 'min_price', 'max_price', 'price'] ); return $products; @@ -246,6 +246,10 @@ private function setPrices(Product $product, ?string $groupedPriceType, ?string if ($this->finalPrice === null && $this->price !== null) { $this->finalPrice = $this->price; } + + if ($this->price == '0.0000' && $this->finalPrice > 0) { + $this->price = $this->finalPrice; + } } /** diff --git a/Service/ProductData/AttributeCollector/Data/Stock.php b/Service/ProductData/AttributeCollector/Data/Stock.php index 2a21a2a..a61a7b3 100755 --- a/Service/ProductData/AttributeCollector/Data/Stock.php +++ b/Service/ProductData/AttributeCollector/Data/Stock.php @@ -231,6 +231,10 @@ private function getNoMsiStock(bool $addMsi = false): array ['catalog_product_entity' => $this->resource->getTableName('catalog_product_entity')], "catalog_product_entity.entity_id = cataloginventory_stock_item.product_id", ['sku'] + )->joinLeft( + ['css' => $this->resource->getTableName('cataloginventory_stock_status')], + 'css.product_id = catalog_product_entity.entity_id', + ['stock_status'] ); if ($addMsi) { $select->joinLeft( @@ -247,8 +251,8 @@ private function getNoMsiStock(bool $addMsi = false): array $result[$value['product_id']] = [ 'qty' => (int)$value['qty'], - 'is_in_stock' => (int)$value['is_in_stock'], - 'availability' => (int)$value['is_in_stock'], + 'is_in_stock' => (int)$value['stock_status'], + 'availability' => (int)$value['stock_status'], 'manage_stock' => (int)$value['manage_stock'], 'qty_increments' => (int)$value['qty_increments'], 'min_sale_qty' => (int)$value['min_sale_qty'] diff --git a/Service/ProductData/Filter.php b/Service/ProductData/Filter.php index 0378ed0..3011908 100755 --- a/Service/ProductData/Filter.php +++ b/Service/ProductData/Filter.php @@ -61,16 +61,13 @@ public function __construct( public function execute(array $filter, int $storeId = 0): array { $entityIds = $this->filterVisibility($filter); + $entityIds = $this->filterStatus($entityIds, $filter['add_disabled_products']); if ($storeId) { $websiteId = $this->getWebsiteId($storeId); $entityIds = $this->filterWebsite($entityIds, $websiteId); } - if (!$filter['add_disabled_products']) { - $entityIds = $this->filterEnabledStatus($entityIds); - } - if ($filter['restrict_by_category']) { $entityIds = $this->filterByCategories( $entityIds, @@ -169,10 +166,15 @@ private function filterWebsite(array $entityIds, int $websiteId): array * Filter entity ids to exclude products with status disabled * * @param array $entityIds + * @param bool $addDisabled * @return array */ - private function filterEnabledStatus(array $entityIds): array + private function filterStatus(array $entityIds, bool $addDisabled = false): array { + $status = $addDisabled + ? [Status::STATUS_ENABLED, Status::STATUS_DISABLED] + : [Status::STATUS_ENABLED]; + $connection = $this->resourceConnection->getConnection(); $select = $connection->select()->distinct()->from( ['catalog_product_entity_int' => $this->resourceConnection->getTableName('catalog_product_entity_int')], @@ -182,8 +184,8 @@ private function filterEnabledStatus(array $entityIds): array 'eav_attribute.attribute_id = catalog_product_entity_int.attribute_id', [] )->where( - 'value = ?', - Status::STATUS_ENABLED + 'value in (?)', + $status )->where( 'attribute_code = ?', 'status' diff --git a/Service/ProductData/Type.php b/Service/ProductData/Type.php index fa1a566..97990b1 100755 --- a/Service/ProductData/Type.php +++ b/Service/ProductData/Type.php @@ -49,6 +49,7 @@ public function __construct( * @param array $attributeMap * @param array $extraParameters * @param int $storeId + * @param bool $addDisabled * @return array * @throws NoSuchEntityException */ @@ -62,7 +63,7 @@ public function execute( return []; } - $parents = $this->parents->execute(); + $parents = $this->parents->execute($entityIds, $extraParameters['filters']['exclude_disabled']); $toUnset = []; $parentAttributeToUse = []; $extraProductsToLoad = []; diff --git a/composer.json b/composer.json index e9b2748..8361ab3 100755 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "magmodules/magento2-sooqr", "description": "Sooqr integration for Magento 2", "type": "magento2-module", - "version": "2.0.4", + "version": "2.0.5", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/etc/config.xml b/etc/config.xml index 5ed27fa..873bd5d 100755 --- a/etc/config.xml +++ b/etc/config.xml @@ -11,7 +11,7 @@ 1 - v2.0.4 + v2.0.5 production