diff --git a/modules/promotion/commerce_promotion.install b/modules/promotion/commerce_promotion.install index 56f127fc..9ff16ff3 100644 --- a/modules/promotion/commerce_promotion.install +++ b/modules/promotion/commerce_promotion.install @@ -160,3 +160,11 @@ function commerce_promotion_update_8205() { $entity_definition_update->installFieldStorageDefinition('usage_limit_customer', 'commerce_promotion', 'commerce_promotion', $storage_definition); $entity_definition_update->installFieldStorageDefinition('usage_limit_customer', 'commerce_promotion_coupon', 'commerce_promotion', $storage_definition); } + +/** + * Force block_rebuild() to run for updated 'stores' field. + */ +function commerce_promotion_update_8206() { + // An empty update flushes caches, forcing block_rebuild() to run. + ; +} diff --git a/modules/promotion/src/Entity/Promotion.php b/modules/promotion/src/Entity/Promotion.php index db405605..cb150dd9 100644 --- a/modules/promotion/src/Entity/Promotion.php +++ b/modules/promotion/src/Entity/Promotion.php @@ -483,7 +483,7 @@ class Promotion extends CommerceContentEntityBase implements PromotionInterface if (!in_array($order->bundle(), $this->getOrderTypeIds())) { return FALSE; } - if (!in_array($order->getStoreId(), $this->getStoreIds())) { + if (!empty($this->getStoreIds()) && !in_array($order->getStoreId(), $this->getStoreIds())) { return FALSE; } $date = $order->getCalculationDate(); @@ -680,9 +680,8 @@ class Promotion extends CommerceContentEntityBase implements PromotionInterface $fields['stores'] = BaseFieldDefinition::create('entity_reference') ->setLabel(t('Stores')) - ->setDescription(t('The stores for which the promotion is valid.')) + ->setDescription(t('Limit promotion availability to selected stores.')) ->setCardinality(BaseFieldDefinition::CARDINALITY_UNLIMITED) - ->setRequired(TRUE) ->setSetting('target_type', 'commerce_store') ->setSetting('handler', 'default') ->setDisplayOptions('form', [ diff --git a/modules/promotion/src/PromotionStorage.php b/modules/promotion/src/PromotionStorage.php index c559992b..4d63c0c2 100644 --- a/modules/promotion/src/PromotionStorage.php +++ b/modules/promotion/src/PromotionStorage.php @@ -98,12 +98,15 @@ class PromotionStorage extends CommerceContentEntityStorage implements Promotion $or_condition = $query->orConditionGroup() ->condition('end_date', $date, '>') ->notExists('end_date'); + $store_condition = $query->orConditionGroup() + ->notExists('stores') + ->condition('stores', [$order->getStoreId()], 'IN'); $query - ->condition('stores', [$order->getStoreId()], 'IN') ->condition('order_types', [$order->bundle()], 'IN') ->condition('start_date', $date, '<=') ->condition('status', TRUE) - ->condition($or_condition); + ->condition($or_condition) + ->condition($store_condition); if ($offer_ids) { $query->condition('offer.target_plugin_id', $offer_ids, 'IN'); } diff --git a/modules/promotion/tests/src/Kernel/Entity/CouponTest.php b/modules/promotion/tests/src/Kernel/Entity/CouponTest.php index 82cb4882..1397cc66 100644 --- a/modules/promotion/tests/src/Kernel/Entity/CouponTest.php +++ b/modules/promotion/tests/src/Kernel/Entity/CouponTest.php @@ -149,4 +149,54 @@ class CouponTest extends OrderKernelTestBase { $this->assertFalse($coupon->available($order)); } + /** + * @covers ::available + */ + public function testAvailabilityAllStores() { + // Test availability for promotions available in all stores + $order_item = OrderItem::create([ + 'type' => 'test', + 'quantity' => 1, + 'unit_price' => new Price('12.00', 'USD'), + ]); + $order_item->save(); + $order = Order::create([ + 'type' => 'default', + 'state' => 'draft', + 'mail' => 'test@example.com', + 'ip_address' => '127.0.0.1', + 'order_number' => '6', + 'store_id' => $this->store, + 'uid' => $this->createUser(), + 'order_items' => [$order_item], + ]); + $order->setRefreshState(Order::REFRESH_SKIP); + $order->save(); + + $promotion = Promotion::create([ + 'order_types' => ['default'], + 'stores' => [], + 'usage_limit' => 1, + 'start_date' => '2017-01-01', + 'status' => TRUE, + ]); + $promotion->save(); + + $coupon = Coupon::create([ + 'promotion_id' => $promotion->id(), + 'code' => 'coupon_code', + 'usage_limit' => 1, + 'status' => TRUE, + ]); + $coupon->save(); + $this->assertTrue($coupon->available($order)); + + $coupon->setEnabled(FALSE); + $this->assertFalse($coupon->available($order)); + $coupon->setEnabled(TRUE); + + \Drupal::service('commerce_promotion.usage')->register($order, $promotion, $coupon); + $this->assertFalse($coupon->available($order)); + } + } diff --git a/modules/promotion/tests/src/Kernel/PromotionAvailabilityTest.php b/modules/promotion/tests/src/Kernel/PromotionAvailabilityTest.php index 48219500..0806e962 100644 --- a/modules/promotion/tests/src/Kernel/PromotionAvailabilityTest.php +++ b/modules/promotion/tests/src/Kernel/PromotionAvailabilityTest.php @@ -94,6 +94,33 @@ class PromotionAvailabilityTest extends OrderKernelTestBase { $promotion->setStoreIds([$this->store->id()]); } + /** + * Test availability for promotions available in all stores. + */ + public function testAvailabilityAllStores() { + $promotion = Promotion::create([ + 'order_types' => ['default'], + 'stores' => [], + 'usage_limit' => 2, + 'start_date' => '2019-01-01T00:00:00', + 'status' => TRUE, + ]); + $promotion->save(); + $this->assertTrue($promotion->available($this->order)); + + $promotion->setEnabled(FALSE); + $this->assertFalse($promotion->available($this->order)); + $promotion->setEnabled(TRUE); + + $promotion->setOrderTypeIds(['test']); + $this->assertFalse($promotion->available($this->order)); + $promotion->setOrderTypeIds(['default']); + + $promotion->setStoreIds(['90']); + $this->assertFalse($promotion->available($this->order)); + $promotion->setStoreIds([$this->store->id()]); + } + /** * Tests the start date logic. */ diff --git a/modules/promotion/tests/src/Kernel/PromotionStorageTest.php b/modules/promotion/tests/src/Kernel/PromotionStorageTest.php index 4aa33d34..1b43a174 100644 --- a/modules/promotion/tests/src/Kernel/PromotionStorageTest.php +++ b/modules/promotion/tests/src/Kernel/PromotionStorageTest.php @@ -186,13 +186,30 @@ class PromotionStorageTest extends OrderKernelTestBase { 'status' => TRUE, ]); $promotion6->save(); + // Past start date, enabled. No end time. All stores. + $promotion7 = Promotion::create([ + 'name' => 'Promotion 7', + 'order_types' => [$this->orderType], + 'start_date' => '2014-01-01T00:00:00', + 'status' => TRUE, + ]); + $this->assertEquals(SAVED_NEW, $promotion7->save()); + // Past start date, enabled. No end time. Different store. + $promotion8 = Promotion::create([ + 'name' => 'Promotion 8', + 'order_types' => [$this->orderType], + 'stores' => [5], + 'start_date' => '2014-01-01T00:00:00', + 'status' => TRUE, + ]); + $this->assertEquals(SAVED_NEW, $promotion8->save()); // Confirm that the promotions were filtered by date and status, // and sorted by weight. $promotions = $this->promotionStorage->loadAvailable($this->order); - $this->assertCount(3, $promotions); + $this->assertCount(4, $promotions); $this->assertEquals([ - $promotion5->id(), $promotion1->id(), $promotion2->id(), + $promotion5->id(), $promotion1->id(), $promotion2->id(), $promotion7->id() ], array_keys($promotions)); // Test filtering by offer ID.