From 07bc44a3d9c01707fac0c4b1f00fffb76ccef2f4 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Wed, 15 Nov 2023 13:25:15 +0000 Subject: [PATCH] Fixed #3328 element site status and sales --- CHANGELOG.md | 3 +- src/elements/db/VariantQuery.php | 107 ++++++++++++++++--------------- src/services/Sales.php | 30 +++++++-- 3 files changed, 81 insertions(+), 59 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20c07b9191..214521d647 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,8 @@ - Improved the performance of the `craft\commerce\elements\db\VariantQuery::hasProduct()` and `craft\commerce\elements\db\ProductQuery::hasVariant()` query params. ([#3325](https://github.com/craftcms/commerce/pull/3325)) - Order statuses with long names no longer line wrap on the Orders index page. ([#3335](https://github.com/craftcms/commerce/issues/3335)) - Fixed duplicate validate errors that occurred when submitting a zero quantity to the cart. ([3334](https://github.com/craftcms/commerce/issues/3334)) -- Fixed a bug where tab selection was inconsistent on the Edit Order page. +- Fixed a bug where tab selection was inconsistent on the Edit Order page. +- Fixed a bug where sales weren’t respecting site element statuses. ([#3328](https://github.com/craftcms/commerce/issues/3328)) ## 4.3.2 - 2023-10-31 diff --git a/src/elements/db/VariantQuery.php b/src/elements/db/VariantQuery.php index d5cadac2cd..1ee6804bbb 100644 --- a/src/elements/db/VariantQuery.php +++ b/src/elements/db/VariantQuery.php @@ -783,21 +783,21 @@ protected function beforePrepare(): bool // Check to see if we have any sales that match all products and categories // so we can skip extra processing if needed $allProductsAndCategoriesSales = ArrayHelper::whereMultiple($activeSales, ['allPurchasables' => 1, 'allCategories' => 1]); + $hasSalesVariantConditions = []; + $hasSalesProductConditions = []; if (empty($allProductsAndCategoriesSales)) { $purchasableRestrictedSales = ArrayHelper::whereMultiple($activeSales, ['allPurchasables' => 0]); $categoryRestrictedSales = ArrayHelper::whereMultiple($activeSales, ['allCategories' => 0]); - $purchasableRestrictedIds = (new Query()) + $purchasableRestrictedQuery = (new Query()) ->select('purchasableId') ->from(Table::SALE_PURCHASABLES . ' sp') ->where([ 'saleId' => ArrayHelper::getColumn($purchasableRestrictedSales, 'id'), - ]) - ->column(); + ]); + $hasSalesVariantConditions[] = ['commerce_variants.id' => $purchasableRestrictedQuery]; - $categoryRestrictedVariantIds = []; - $categoryRestrictedProductIds = []; if (!empty($categoryRestrictedSales)) { $sourceSales = ArrayHelper::whereMultiple($categoryRestrictedSales, [ 'categoryRelationshipType' => [ @@ -813,69 +813,70 @@ protected function beforePrepare(): bool ]); // Source relationships - $sourceVariantIds = []; - $sourceProductIds = []; if (!empty($sourceSales)) { - $sourceRows = (new Query()) - ->select('elements.type, rel.sourceId') + $sourceQueryProduct = (new Query()) + ->select('rel.sourceId') + ->from(Table::SALE_CATEGORIES . ' sc') + ->leftJoin(CraftTable::RELATIONS . ' rel', '[[rel.targetId]] = [[sc.categoryId]]') + ->leftJoin(CraftTable::ELEMENTS . ' elements', '[[elements.id]] = [[rel.sourceId]]') + ->leftJoin(CraftTable::ELEMENTS_SITES . ' es', '[[es.elementId]] = [[sc.categoryId]]') + ->where(['saleId' => ArrayHelper::getColumn($sourceSales, 'id')]) + ->andWhere(['elements.type' => Product::class]) + ->andWhere(Db::parseParam('es.siteId', $this->siteId)) + ->andWhere(['es.enabled' => true]); + $hasSalesProductConditions[] = ['commerce_variants.productId' => $sourceQueryProduct]; + + $sourceQueryVariant = (new Query()) + ->select('rel.sourceId') ->from(Table::SALE_CATEGORIES . ' sc') ->leftJoin(CraftTable::RELATIONS . ' rel', '[[rel.targetId]] = [[sc.categoryId]]') ->leftJoin(CraftTable::ELEMENTS . ' elements', '[[elements.id]] = [[rel.sourceId]]') - ->where([ - 'saleId' => ArrayHelper::getColumn($sourceSales, 'id'), - ]) - ->all(); - - $sourceProductIds = collect($sourceRows) - ->filter(fn($row) => $row['type'] === Product::class) - ->map(fn($row) => $row['sourceId']) - ->all(); - - $sourceVariantIds = collect($sourceRows) - ->filter(fn($row) => $row['type'] === Variant::class) - ->map(fn($row) => $row['sourceId']) - ->all(); + ->leftJoin(CraftTable::ELEMENTS_SITES . ' es', '[[es.elementId]] = [[sc.categoryId]]') + ->where(['saleId' => ArrayHelper::getColumn($sourceSales, 'id')]) + ->andWhere(['elements.type' => Variant::class]) + ->andWhere(Db::parseParam('es.siteId', $this->siteId)) + ->andWhere(['es.enabled' => true]); + $hasSalesVariantConditions[] = ['commerce_variants.id' => $sourceQueryVariant]; } // Target relationships - $targetVariantIds = []; - $targetProductIds = []; if (!empty($targetSales)) { - $targetRows = (new Query()) - ->select('elements.type, rel.targetId') + $targetQueryProduct = (new Query()) + ->select('rel.targetId') + ->from(Table::SALE_CATEGORIES . ' sc') + ->leftJoin(CraftTable::RELATIONS . ' rel', '[[rel.sourceId]] = [[sc.categoryId]]') + ->leftJoin(CraftTable::ELEMENTS . ' elements', '[[elements.id]] = [[rel.targetId]]') + ->leftJoin(CraftTable::ELEMENTS_SITES . ' es', '[[es.elementId]] = [[sc.categoryId]]') + ->where(['saleId' => ArrayHelper::getColumn($targetSales, 'id')]) + ->andWhere(['elements.type' => Product::class]) + ->andWhere(Db::parseParam('es.siteId', $this->siteId)) + ->andWhere(['es.enabled' => true]); + $hasSalesProductConditions[] = ['commerce_variants.productId' => $targetQueryProduct]; + + $targetQueryVariant = (new Query()) + ->select('rel.targetId') ->from(Table::SALE_CATEGORIES . ' sc') ->leftJoin(CraftTable::RELATIONS . ' rel', '[[rel.sourceId]] = [[sc.categoryId]]') ->leftJoin(CraftTable::ELEMENTS . ' elements', '[[elements.id]] = [[rel.targetId]]') - ->where([ - 'saleId' => ArrayHelper::getColumn($targetSales, 'id'), - ]) - ->all(); - - $targetProductIds = collect($targetRows) - ->filter(fn($row) => $row['type'] === Product::class) - ->map(fn($row) => $row['targetId']) - ->all(); - - $targetVariantIds = collect($targetRows) - ->filter(fn($row) => $row['type'] === Product::class) - ->map(fn($row) => $row['targetId']) - ->all(); + ->leftJoin(CraftTable::ELEMENTS_SITES . ' es', '[[es.elementId]] = [[sc.categoryId]]') + ->where(['saleId' => ArrayHelper::getColumn($targetSales, 'id')]) + ->andWhere(['elements.type' => Variant::class]) + ->andWhere(Db::parseParam('es.siteId', $this->siteId)) + ->andWhere(['es.enabled' => true]); + $hasSalesVariantConditions[] = ['commerce_variants.id' => $targetQueryVariant]; } - - $categoryRestrictedVariantIds = array_merge($sourceVariantIds, $targetVariantIds); - $categoryRestrictedProductIds = array_merge($sourceProductIds, $targetProductIds); } - - $variantIds = array_unique(array_merge($purchasableRestrictedIds, $categoryRestrictedVariantIds)); - $productIds = $categoryRestrictedProductIds; } } - $hasSalesCondition = [ - 'or', - ['commerce_variants.id' => $variantIds], - ['commerce_variants.productId' => $productIds], - ]; + $hasSalesCondition = ['or']; + if (!empty($hasSalesVariantConditions)) { + $hasSalesCondition[] = array_merge(['or'], $hasSalesVariantConditions); + } + + if (!empty($hasSalesProductConditions)) { + $hasSalesCondition[] = array_merge(['or'], $hasSalesProductConditions); + } if ($this->hasSales) { $this->subQuery->andWhere($hasSalesCondition); @@ -930,7 +931,7 @@ private function _applyHasProductParam(): void // Remove any blank product IDs (if any) $productQuery->andWhere(['not', ['commerce_products.id' => null]]); - + $this->subQuery->andWhere(['commerce_variants.productId' => $productQuery]); } diff --git a/src/services/Sales.php b/src/services/Sales.php index 858c491524..8213f135a1 100644 --- a/src/services/Sales.php +++ b/src/services/Sales.php @@ -276,9 +276,13 @@ public function getSalesForPurchasable(PurchasableInterface $purchasable, Order return $matchedSales; } - + /** + * @param PurchasableInterface $purchasable + * @return array + */ public function getSalesRelatedToPurchasable(PurchasableInterface $purchasable): array { + /** @var Purchasable $purchasable */ $sales = []; $id = $purchasable->getId(); @@ -291,8 +295,16 @@ public function getSalesRelatedToPurchasable(PurchasableInterface $purchasable): $relatedTo = [$sale->categoryRelationshipType => $purchasable->getPromotionRelationSource()]; $saleCategories = $sale->getCategoryIds(); - $relatedCategories = Category::find()->id($saleCategories)->relatedTo($relatedTo)->ids(); - $relatedEntries = Entry::find()->id($saleCategories)->relatedTo($relatedTo)->ids(); + $relatedCategories = Category::find() + ->id($saleCategories) + ->relatedTo($relatedTo) + ->siteId($purchasable->siteId) + ->ids(); + $relatedEntries = Entry::find() + ->id($saleCategories) + ->relatedTo($relatedTo) + ->siteId($purchasable->siteId) + ->ids(); $relatedCategoriesOrEntries = array_merge($relatedCategories, $relatedEntries); if (in_array($id, $purchasableIds, false) || !empty($relatedCategoriesOrEntries)) { @@ -453,8 +465,16 @@ public function matchPurchasableAndSale(PurchasableInterface $purchasable, Sale if (!$sale->allCategories) { $relatedTo = [$sale->categoryRelationshipType => $purchasable->getPromotionRelationSource()]; $saleCategories = $sale->getCategoryIds(); - $relatedCategories = Category::find()->id($saleCategories)->relatedTo($relatedTo)->ids(); - $relatedEntries = Entry::find()->id($saleCategories)->relatedTo($relatedTo)->ids(); + $relatedCategories = Category::find() + ->id($saleCategories) + ->relatedTo($relatedTo) + ->siteId($purchasable->siteId) + ->ids(); + $relatedEntries = Entry::find() + ->id($saleCategories) + ->relatedTo($relatedTo) + ->siteId($purchasable->siteId) + ->ids(); $relatedCategoriesOrEntries = array_merge($relatedCategories, $relatedEntries); if (empty($relatedCategoriesOrEntries)) { return false;