objectManager = Bootstrap::getObjectManager(); $this->criteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); $this->quoteRepository = $this->objectManager->get(CartRepositoryInterface::class); $this->fixtures = DataFixtureStorageManager::getStorage(); $this->discountCollector = $this->objectManager->create(Discount::class); $this->subtotalCollector = $this->objectManager->create(Subtotal::class); $this->shippingAssignment = $this->objectManager->create(ShippingAssignment::class); $this->shipping = $this->objectManager->create(Shipping::class); $this->quote = $this->objectManager->get(QuoteRepository::class); $this->total = $this->objectManager->create(Total::class); } /** * @magentoDataFixture Magento/Checkout/_files/quote_with_bundle_product_with_dynamic_price.php * @dataProvider bundleProductWithDynamicPriceAndCartPriceRuleDataProvider * @param string $coupon * @param array $discounts * @param float $totalDiscount * @return void */ #[ AppIsolation(true), DataFixture( ProductConditionFixture::class, ['attribute' => 'sku', 'value' => 'bundle_product_with_dynamic_price'], 'cond1' ), DataFixture( ProductConditionFixture::class, ['attribute' => 'sku', 'value' => 'simple1'], 'cond2' ), DataFixture( ProductConditionFixture::class, ['attribute' => 'sku', 'value' => 'simple2'], 'cond3' ), DataFixture( RuleFixture::class, ['coupon_code' => 'bundle_cc', 'discount_amount' => 50, 'actions' => ['$cond1$']], 'rule1' ), DataFixture( RuleFixture::class, ['coupon_code' => 'simple1_cc', 'discount_amount' => 50, 'actions' => ['$cond2$']], 'rule2' ), DataFixture( RuleFixture::class, ['coupon_code' => 'simple2_cc', 'discount_amount' => 50, 'actions' => ['$cond3$']], 'rule3' ), ] public function testBundleProductWithDynamicPriceAndCartPriceRule( string $coupon, array $discounts, float $totalDiscount ): void { $quote = $this->getQuote('quote_with_bundle_product_with_dynamic_price'); $quote->setCouponCode($coupon); $quote->collectTotals(); $this->quoteRepository->save($quote); $this->assertEquals(21.98, $quote->getBaseSubtotal()); $this->assertEquals($totalDiscount, $quote->getShippingAddress()->getDiscountAmount()); $items = $quote->getAllItems(); $this->assertCount(3, $items); /** @var Item $item*/ $item = array_shift($items); $this->assertEquals('bundle_product_with_dynamic_price-simple1-simple2', $item->getSku()); $this->assertEquals($discounts[$item->getSku()], $item->getDiscountAmount()); $item = array_shift($items); $this->assertEquals('simple1', $item->getSku()); $this->assertEquals(5.99, $item->getPrice()); $this->assertEquals($discounts[$item->getSku()], $item->getDiscountAmount()); $item = array_shift($items); $this->assertEquals('simple2', $item->getSku()); $this->assertEquals(15.99, $item->getPrice()); $this->assertEquals($discounts[$item->getSku()], $item->getDiscountAmount()); } /** * @return array */ public function bundleProductWithDynamicPriceAndCartPriceRuleDataProvider(): array { return [ [ 'bundle_cc', [ 'bundle_product_with_dynamic_price-simple1-simple2' => 10.99, 'simple1' => 0, 'simple2' => 0, ], -10.99 ], [ 'simple1_cc', [ 'bundle_product_with_dynamic_price-simple1-simple2' => 0, 'simple1' => 3, 'simple2' => 0, ], -3 ], [ 'simple2_cc', [ 'bundle_product_with_dynamic_price-simple1-simple2' => 0, 'simple1' => 0, 'simple2' => 8, ], -8 ] ]; } /** * @param string $reservedOrderId * @return Quote */ private function getQuote(string $reservedOrderId): Quote { $searchCriteria = $this->criteriaBuilder->addFilter('reserved_order_id', $reservedOrderId) ->create(); $carts = $this->quoteRepository->getList($searchCriteria) ->getItems(); return array_shift($carts); } /** * @return void * @throws NoSuchEntityException */ #[ DataFixture(CategoryFixture::class, as: 'c1'), DataFixture(CategoryFixture::class, as: 'c2'), DataFixture(CategoryFixture::class, as: 'c3'), DataFixture(ProductFixture::class, [ 'price' => 40, 'sku' => 'p1', 'category_ids' => ['$c1.id$'] ], 'p1'), DataFixture(ProductFixture::class, [ 'price' => 30, 'sku' => 'p2', 'category_ids' => ['$c1.id$', '$c2.id$'] ], 'p2'), DataFixture(ProductFixture::class, [ 'price' => 20, 'sku' => 'p3', 'category_ids' => ['$c2.id$', '$c3.id$'] ], 'p3'), DataFixture(ProductFixture::class, [ 'price' => 10, 'sku' => 'p4', 'category_ids' => ['$c3.id$'] ], 'p4'), DataFixture( ProductConditionFixture::class, [ 'attribute' => 'category_ids', 'value' => '$c1.id$', 'operator' => '==', 'conditions' => [ '1' => [ 'type' => CombineCondition::class, 'aggregator' => 'all', 'value' => '1', 'new_child' => '', ], '1--1' => [ 'type' => ProductCondition::class, 'attribute' => 'category_ids', 'operator' => '==', 'value' => '$c1.id$', ] ], ], 'cond1' ), DataFixture( ProductConditionFixture::class, [ 'attribute' => 'category_ids', 'value' => '$c2.id$', 'operator' => '==', 'conditions' => [ '1' => [ 'type' => CombineCondition::class, 'aggregator' => 'all', 'value' => '1', 'new_child' => '', ], '1--1' => [ 'type' => ProductCondition::class, 'attribute' => 'category_ids', 'operator' => '==', 'value' => '$c2.id$', ] ], ], 'cond2' ), DataFixture( ProductConditionFixture::class, [ 'attribute' => 'category_ids', 'value' => '$c3.id$', 'operator' => '==', 'conditions' => [ '1' => [ 'type' => CombineCondition::class, 'aggregator' => 'all', 'value' => '1', 'new_child' => '', ], '1--1' => [ 'type' => ProductCondition::class, 'attribute' => 'category_ids', 'operator' => '==', 'value' => '$c3.id$', ] ], ], 'cond3' ), DataFixture( RuleFixture::class, [ 'stop_rules_processing'=> 0, 'coupon_code' => 'test', 'discount_amount' => 10, 'actions' => ['$cond1$'], 'simple_action' => Rule::BY_FIXED_ACTION, 'sort_order' => 0 ], 'rule1' ), DataFixture( RuleFixture::class, [ 'discount_amount' => 5, 'actions' => ['$cond2$'], 'simple_action' => Rule::BY_FIXED_ACTION, 'sort_order' => 1 ], 'rule2' ), DataFixture( RuleFixture::class, [ 'stop_rules_processing'=> 0, 'discount_amount' => 2, 'actions' => ['$cond3$'], 'simple_action' => Rule::BY_FIXED_ACTION, 'sort_order' => 2 ], 'rule3' ), DataFixture(GuestCartFixture::class, as: 'cart'), DataFixture(AddProductToCartFixture::class, ['cart_id' => '$cart.id$', 'product_id' => '$p1.id$']), DataFixture(AddProductToCartFixture::class, ['cart_id' => '$cart.id$', 'product_id' => '$p2.id$']), DataFixture(AddProductToCartFixture::class, ['cart_id' => '$cart.id$', 'product_id' => '$p3.id$']), DataFixture(AddProductToCartFixture::class, ['cart_id' => '$cart.id$', 'product_id' => '$p4.id$']) ] public function testDiscountOnSimpleProductWithDiscardSubsequentRule(): void { $cartId = (int)$this->fixtures->get('cart')->getId(); $rule1Id = (int)$this->fixtures->get('rule1')->getId(); $rule2Id = (int)$this->fixtures->get('rule2')->getId(); $rule3Id = (int)$this->fixtures->get('rule3')->getId(); $product1Id = (int) $this->fixtures->get('p1')->getId(); $product2Id = (int) $this->fixtures->get('p2')->getId(); $product3Id = (int) $this->fixtures->get('p3')->getId(); $product4Id = (int) $this->fixtures->get('p4')->getId(); $quote = $this->quote->get($cartId); $quote->setStoreId(1)->setIsActive(true)->setIsMultiShipping(0)->setCouponCode('test'); $address = $quote->getShippingAddress(); $this->shipping->setAddress($address); $this->shippingAssignment->setShipping($this->shipping); $this->shippingAssignment->setItems($address->getAllItems()); $this->subtotalCollector->collect($quote, $this->shippingAssignment, $this->total); $this->discountCollector->collect($quote, $this->shippingAssignment, $this->total); $this->assertEquals(-32, $this->total->getDiscountAmount()); $items = []; foreach ($quote->getAllItems() as $item) { $items[$item->getProductId()] = $item; } $this->assertEqualsCanonicalizing([$rule1Id,$rule2Id,$rule3Id], explode(',', $quote->getAppliedRuleIds())); $this->assertEqualsCanonicalizing([$rule1Id,$rule2Id,$rule3Id], explode(',', $address->getAppliedRuleIds())); $this->assertEqualsCanonicalizing([$rule1Id], explode(',', $items[$product1Id]->getAppliedRuleIds())); $this->assertEqualsCanonicalizing([$rule1Id,$rule2Id], explode(',', $items[$product2Id]->getAppliedRuleIds())); $this->assertEqualsCanonicalizing([$rule2Id], explode(',', $items[$product3Id]->getAppliedRuleIds())); $this->assertEqualsCanonicalizing([$rule3Id], explode(',', $items[$product4Id]->getAppliedRuleIds())); } #[ AppIsolation(true), DataFixture(AttributeFixture::class, ['options' => [['label' => 'option1', 'sort_order' => 0]]], as: 'attr'), DataFixture(ProductFixture::class, ['price' => 100], as: 'p1'), DataFixture(ConfigurableProductFixture::class, ['_options' => ['$attr$'], '_links' => ['$p1$']], 'cp1'), DataFixture( ProductConditionFixture::class, ['attribute' => 'sku', 'value' => '$p1.sku$'], 'cond1' ), DataFixture( RuleFixture::class, ['simple_action' => Rule::CART_FIXED_ACTION, 'discount_amount' => 50, 'actions' => ['$cond1$']], 'rule1' ), DataFixture(GuestCartFixture::class, as: 'cart'), DataFixture( AddConfigurableProductToCartFixture::class, ['cart_id' => '$cart.id$', 'product_id' => '$cp1.id$', 'child_product_id' => '$p1.id$', 'qty' => 1], ) ] public function testFixedAmountWholeCartDiscountOnConfigurableProduct(): void { $quote = $this->fixtures->get('cart'); $this->assertEquals(50, $quote->getGrandTotal()); $this->assertEquals(50, $quote->getSubtotalWithDiscount()); $this->assertEquals(100, $quote->getSubtotal()); $quote->getAllItems(); //emulate a plugin on afterGetPrice foreach ($quote->getAllItems() as $item) { /** @var $item \Magento\Quote\Model\Quote\Item */ $item->setPrice(200); } $quote->collectTotals(); $this->assertEquals(50, $quote->getGrandTotal()); $this->assertEquals(50, $quote->getSubtotalWithDiscount()); $this->assertEquals(100, $quote->getSubtotal()); } #[ AppIsolation(true), DataFixture(ProductFixture::class, ['sku' => 'simple1', 'price' => 10], as:'p1'), DataFixture(ProductFixture::class, ['sku' => 'simple2', 'price' => 20], as:'p2'), DataFixture(BundleSelectionFixture::class, ['sku' => '$p1.sku$', 'price' => 10, 'price_type' => 0], as:'link1'), DataFixture(BundleSelectionFixture::class, ['sku' => '$p2.sku$', 'price' => 25, 'price_type' => 1], as:'link2'), DataFixture(BundleOptionFixture::class, ['title' => 'Checkbox Options', 'type' => 'checkbox', 'required' => 1,'product_links' => ['$link1$', '$link2$']], 'opt1'), DataFixture(BundleOptionFixture::class, ['title' => 'Multiselect Options', 'type' => 'multi', 'required' => 1,'product_links' => ['$link1$', '$link2$']], 'opt2'), DataFixture( BundleProductFixture::class, ['sku' => 'bundle-product-multiselect-checkbox-options','price' => 50,'price_type' => 1, '_options' => ['$opt1$', '$opt2$']], as:'bp1' ), DataFixture( ProductConditionFixture::class, ['attribute' => 'sku', 'value' => 'bundle-product-multiselect-checkbox-options'], as:'cond1' ), DataFixture( RuleFixture::class, ['simple_action' => Rule::CART_FIXED_ACTION, 'discount_amount' => 50, 'actions' => ['$cond1$']], as:'rule1' ), DataFixture(GuestCartFixture::class, as: 'cart'), DataFixture( AddBundleProductToCart::class, [ 'cart_id' => '$cart.id$', 'product_id' => '$bp1.id$', 'selections' => [['$p1.id$'], ['$p1.id$', '$p2.id$']], 'qty' => 1 ], ) ] public function testFixedAmountWholeCartDiscountOnBundleProduct(): void { $quote = $this->fixtures->get('cart'); $this->assertEquals(32.5, $quote->getGrandTotal()); $this->assertEquals(32.5, $quote->getSubtotalWithDiscount()); $this->assertEquals(82.5, $quote->getSubtotal()); } }