![]() Server : Apache System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64 User : corals ( 1002) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/corals/old/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/ |
<?php /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ declare(strict_types=1); namespace Magento\Bundle\Model\Product; use Magento\Bundle\Test\Fixture\Link as BundleSelectionFixture; use Magento\Bundle\Test\Fixture\Option as BundleOptionFixture; use Magento\Bundle\Test\Fixture\Product as BundleProductFixture; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Api\Data\TierPriceInterface; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Test\Fixture\Product as ProductFixture; use Magento\Customer\Model\Group; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Serialize\SerializerInterface; use Magento\Store\Api\WebsiteRepositoryInterface; use Magento\TestFramework\Catalog\Model\GetCategoryByName; use Magento\TestFramework\Catalog\Model\Product\Price\GetPriceIndexDataByProductId; use Magento\TestFramework\Fixture\DataFixture; use Magento\TestFramework\Helper\Bootstrap; use PHPUnit\Framework\TestCase; /** * Class to test bundle prices * * @magentoDbIsolation disabled * @magentoAppIsolation enabled * @magentoAppArea frontend * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class PriceTest extends TestCase { /** @var ObjectManagerInterface */ private $objectManager; /** @var ProductRepositoryInterface */ private $productRepository; /** @var GetPriceIndexDataByProductId */ private $getPriceIndexDataByProductId; /** @var WebsiteRepositoryInterface */ private $websiteRepository; /** @var Price */ private $priceModel; /** @var SerializerInterface */ private $json; /** @var GetCategoryByName */ private $getCategoryByName; /** * @inheritdoc */ protected function setUp(): void { $this->objectManager = Bootstrap::getObjectManager(); $this->priceModel = $this->objectManager->get(Price::class); $this->websiteRepository = $this->objectManager->get(WebsiteRepositoryInterface::class); $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); $this->productRepository->cleanCache(); $this->getPriceIndexDataByProductId = $this->objectManager->get(GetPriceIndexDataByProductId::class); $this->json = $this->objectManager->get(SerializerInterface::class); $this->getCategoryByName = $this->objectManager->get(GetCategoryByName::class); } /** * @magentoDataFixture Magento/Bundle/_files/product_with_tier_pricing.php * * @return void */ public function testGetTierPrice(): void { $product = $this->productRepository->get('bundle-product'); // Note that this is really not the "tier price" but the "tier discount percentage" // so it is expected to be increasing instead of decreasing $this->assertEquals(8.0, $this->priceModel->getTierPrice(2, $product)); $this->assertEquals(20.0, $this->priceModel->getTierPrice(3, $product)); $this->assertEquals(20.0, $this->priceModel->getTierPrice(4, $product)); $this->assertEquals(30.0, $this->priceModel->getTierPrice(5, $product)); } /** * Test calculation final price for bundle product with tire price in simple product * @magentoDataFixture Magento/Bundle/_files/product_with_simple_tier_pricing.php * @dataProvider getSelectionFinalTotalPriceWithSimpleTierPriceDataProvider * * @param float $bundleQty * @param float $selectionQty * @param float $finalPrice * @return void */ public function testGetSelectionFinalTotalPriceWithSimpleTierPrice( float $bundleQty, float $selectionQty, float $finalPrice ): void { $bundleProduct = $this->productRepository->get('bundle-product'); $simpleProduct = $this->productRepository->get('simple'); $simpleProduct->setCustomerGroupId(Group::CUST_GROUP_ALL); $this->assertEquals( $finalPrice, $this->priceModel->getSelectionFinalTotalPrice( $bundleProduct, $simpleProduct, $bundleQty, $selectionQty, false ), 'Tier price calculation for Simple product is wrong' ); } /** * @return array */ public function getSelectionFinalTotalPriceWithSimpleTierPriceDataProvider(): array { return [ [1, 1, 10], [2, 1, 8], [5, 1, 5], ]; } /** * Fixed Bundle Product with catalog price rule * @magentoDataFixture Magento/Bundle/_files/fixed_bundle_product_without_discounts.php * @magentoDataFixture Magento/CatalogRule/_files/rule_apply_as_percentage_of_original_not_logged_user.php * * @return void */ public function testFixedBundleProductPriceWithCatalogRule(): void { $this->checkBundlePrices( 'fixed_bundle_product_without_discounts', ['price' => 50, 'final_price' => 45, 'min_price' => 45, 'max_price' => 75, 'tier_price' => null], ['simple1' => 55, 'simple2' => 56.25, 'simple3' => 70] ); } /** * Fixed Bundle Product without discounts * * @return void */ #[ DataFixture(ProductFixture::class, ['sku' => 'simple1', 'price' => 10], 'p1'), DataFixture(ProductFixture::class, ['sku' => 'simple2', 'price' => 20], 'p2'), DataFixture(ProductFixture::class, ['sku' => 'simple3', 'price' => 30], 'p3'), DataFixture(BundleSelectionFixture::class, ['sku' => '$p1.sku$', 'price' => 10, 'price_type' => 0], 'link1'), DataFixture(BundleSelectionFixture::class, ['sku' => '$p2.sku$', 'price' => 25, 'price_type' => 1], 'link2'), DataFixture(BundleSelectionFixture::class, ['sku' => '$p3.sku$', 'price' => 25, 'price_type' => 0], 'link3'), DataFixture(BundleOptionFixture::class, ['product_links' => ['$link1$', '$link2$', '$link3$']], 'opt1'), DataFixture( BundleProductFixture::class, ['sku' => 'bundle1','price' => 50,'price_type' => 1,'_options' => ['$opt1$']], 'bundle1' ), ] public function testFixedBundleProductPriceWithoutDiscounts(): void { $this->checkBundlePrices( 'bundle1', ['price' => 50, 'final_price' => 50, 'min_price' => 60, 'max_price' => 75, 'tier_price' => null], ['simple1' => 60, 'simple2' => 62.5, 'simple3' => 75] ); } /** * Fixed Bundle Product with special price * @magentoDataFixture Magento/Bundle/_files/fixed_bundle_product_with_special_price.php * * @return void */ public function testFixedBundleProductPriceWithSpecialPrice(): void { $this->checkBundlePrices( 'fixed_bundle_product_with_special_price', ['price' => 50, 'final_price' => 40, 'min_price' => 48, 'max_price' => 60, 'tier_price' => null], ['simple1' => 48, 'simple2' => 50, 'simple3' => 60] ); } /** * Fixed Bundle Product with tier price * @magentoDataFixture Magento/Bundle/_files/fixed_bundle_product_with_tier_price.php * * @return void */ public function testFixedBundleProductPriceWithTierPrice(): void { $this->checkBundlePrices( 'fixed_bundle_product_with_tier_price', ['price' => 50, 'final_price' => 50, 'min_price' => 60, 'max_price' => 75, 'tier_price' => 60], ['simple1' => 45, 'simple2' => 46.88, 'simple3' => 56.25] ); } /** * Dynamic Bundle Product without discount + options without discounts * * @return void */ #[ DataFixture(ProductFixture::class, ['sku' => 'simple1000', 'price' => 10], 'p1'), DataFixture(ProductFixture::class, ['sku' => 'simple1001', 'price' => 20], 'p2'), DataFixture(BundleOptionFixture::class, ['product_links' => ['$p1$', '$p2$']], 'opt1'), DataFixture( BundleProductFixture::class, ['sku' => 'bundle1', '_options' => ['$opt1$']], 'bundle1' ), ] public function testDynamicBundleProductWithoutDiscountAndOptionsWithoutDiscounts(): void { $this->checkBundlePrices( 'bundle1', ['price' => 0, 'final_price' => 0, 'min_price' => 10, 'max_price' => 20, 'tier_price' => null], ['simple1000' => 10, 'simple1001' => 20] ); } /** * Dynamic Bundle Product without discount + options with special price * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_without_discounts.php * * @return void */ public function testDynamicBundleProductWithoutDiscountsAndOptionsWithSpecialPrices(): void { $this->updateProducts($this->specialPricesForOptionsData()); $this->checkBundlePrices( 'dynamic_bundle_product_without_discounts', ['price' => 0, 'final_price' => 0, 'min_price' => 8, 'max_price' => 15, 'tier_price' => null], ['simple1000' => 8, 'simple1001' => 15] ); } /** * Dynamic Bundle Product without discount + options with tier prices * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_without_discounts.php * * @return void */ public function testDynamicBundleProductWithoutDiscountsAndOptionsWithTierPrices(): void { $this->updateProducts($this->tierPricesForOptionsData()); $this->checkBundlePrices( 'dynamic_bundle_product_without_discounts', ['price' => 0, 'final_price' => 0, 'min_price' => 8, 'max_price' => 17, 'tier_price' => null], ['simple1000' => 8, 'simple1001' => 17] ); } /** * Dynamic Bundle Product without discounts + options with catalog rule * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_without_discounts.php * @magentoDataFixture Magento/CatalogRule/_files/catalog_rule_for_category_999.php * * @return void */ public function testDynamicBundleProductWithoutDiscountsAndOptionsWithCatalogPriceRule(): void { $this->checkBundlePrices( 'dynamic_bundle_product_without_discounts', ['price' => 0, 'final_price' => 0, 'min_price' => 7.5, 'max_price' => 15, 'tier_price' => null], ['simple1000' => 7.5, 'simple1001' => 15] ); } /** * Dynamic Bundle Product with tier price + options without discounts * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_with_tier_price.php * * @return void */ public function testDynamicBundleProductWithTierPriceAndOptionsWithoutDiscounts(): void { $this->checkBundlePrices( 'dynamic_bundle_product_with_tier_price', ['price' => 0,'final_price' => 0, 'min_price' => 10, 'max_price' => 20, 'tier_price' => 10], ['simple1000' => 7.5, 'simple1001' => 15] ); } /** * Dynamic Bundle Product with tier price + options with special prices * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_with_tier_price.php * * @return void */ public function testDynamicBundleProductWithTierPriceAndOptionsWithSpecialPrices(): void { $this->updateProducts($this->specialPricesForOptionsData()); $this->checkBundlePrices( 'dynamic_bundle_product_with_tier_price', ['price' => 0, 'final_price' => 0, 'min_price' => 8, 'max_price' => 15, 'tier_price' => 8], ['simple1000' => 6, 'simple1001' => 11.25] ); } /** * Dynamic Bundle Product with tier price + options with tier price * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_with_tier_price.php * * @return void */ public function testDynamicBundleProductWithTierPriceAndOptionsWithTierPrices(): void { $this->updateProducts($this->tierPricesForOptionsData()); $this->checkBundlePrices( 'dynamic_bundle_product_with_tier_price', ['price' => 0, 'final_price' => 0, 'min_price' => 8, 'max_price' => 17, 'tier_price' => 8], ['simple1000' => 6, 'simple1001' => 12.75] ); } /** * Dynamic Bundle Product with tier price + options with catalog rule * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_with_tier_price.php * @magentoDataFixture Magento/CatalogRule/_files/catalog_rule_for_category_999.php * * @return void */ public function testDynamicBundleProductWithTierPriceAndOptionsWithCatalogPriceRule(): void { $this->checkBundlePrices( 'dynamic_bundle_product_with_tier_price', ['price' => 0, 'final_price' => 0, 'min_price' => 7.5, 'max_price' => 15, 'tier_price' => 7.5], ['simple1000' => 5.63, 'simple1001' => 11.25] ); } /** * Dynamic Bundle Product with special price + options without discounts * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_with_special_price.php * * @return void */ public function testDynamicBundleProductWithSpecialPriceAndOptionsWithoutDiscounts(): void { $this->checkBundlePrices( 'dynamic_bundle_product_with_special_price', ['price' => 0, 'final_price' => 0, 'min_price' => 7.5, 'max_price' => 15, 'tier_price' => null], ['simple1000' => 7.5, 'simple1001' => 15] ); } /** * Dynamic Bundle Product with special price + options with special prices * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_with_special_price.php * * @return void */ public function testDynamicBundleProductWithSpecialPriceAndOptionsWithSpecialPrices(): void { $this->updateProducts($this->specialPricesForOptionsData()); $this->checkBundlePrices( 'dynamic_bundle_product_with_special_price', ['price' => 0, 'final_price' => 0, 'min_price' => 6, 'max_price' => 11.25, 'tier_price' => null], ['simple1000' => 6, 'simple1001' => 11.25] ); } /** * Dynamic Bundle Product with special price + options with tier prices * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_with_special_price.php * * @return void */ public function testDynamicBundleProductWithSpecialPriceAndOptionsWithTierPrices(): void { $this->updateProducts($this->tierPricesForOptionsData()); $this->checkBundlePrices( 'dynamic_bundle_product_with_special_price', ['price' => 0, 'final_price' => 0, 'min_price' => 6, 'max_price' => 12.75, 'tier_price' => null], ['simple1000' => 6, 'simple1001' => 12.75] ); } /** * Dynamic Bundle Product with special price + options with catalog price rule * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_with_special_price.php * @magentoDataFixture Magento/CatalogRule/_files/catalog_rule_for_category_999.php * * @return void */ public function testDynamicBundleProductWithSpecialPriceAndOptionsWithCatalogPriceRule(): void { $this->checkBundlePrices( 'dynamic_bundle_product_with_special_price', ['price' => 0, 'final_price' => 0, 'min_price' => 5.625, 'max_price' => 11.25, 'tier_price' => null], ['simple1000' => 5.63, 'simple1001' => 11.25] ); } /** * Dynamic Bundle Product with catalog price rule + options without discounts * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_with_catalog_rule.php * * @return void */ public function testDynamicBundleProductWithCatalogPriceRuleAndOptionsWithoutDiscounts(): void { $this->checkBundlePrices( 'dynamic_bundle_product_with_catalog_rule', ['price' => 0, 'final_price' => 0, 'min_price' => 10, 'max_price' => 20, 'tier_price' => null], ['simple1000' => 10, 'simple1001' => 20] ); } /** * Dynamic Bundle Product with catalog price rule + options with catalog price rule * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_with_catalog_rule.php * @magentoDataFixture Magento/CatalogRule/_files/catalog_rule_for_category_999.php * * @return void */ public function testDynamicBundleProductWithCatalogPriceRuleAndOptionsWithCatalogPriceRule(): void { $this->checkBundlePrices( 'dynamic_bundle_product_with_catalog_rule', ['price' => 0, 'final_price' => 0, 'min_price' => 7.5, 'max_price' => 15, 'tier_price' => null], ['simple1000' => 7.5, 'simple1001' => 15] ); } /** * Dynamic Bundle Product with catalog price rule + options with special prices * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_with_catalog_rule.php * * @return void */ public function testDynamicBundleProductWithCatalogPriceRuleAndOptionsWithSpecialPrices(): void { $this->updateProducts($this->specialPricesForOptionsData()); $this->checkBundlePrices( 'dynamic_bundle_product_with_catalog_rule', ['price' => 0, 'final_price' => 0, 'min_price' => 8, 'max_price' => 15, 'tier_price' => null], ['simple1000' => 8, 'simple1001' => 15] ); } /** * Dynamic Bundle Product with catalog price rule + options with tier price * @magentoDataFixture Magento/Bundle/_files/dynamic_bundle_product_with_catalog_rule.php * * @return void */ public function testDynamicBundleProductWithCatalogPriceRuleAndOptionsWithTierPrice(): void { $this->updateProducts($this->tierPricesForOptionsData()); $this->checkBundlePrices( 'dynamic_bundle_product_with_catalog_rule', ['price' => 0, 'final_price' => 0, 'min_price' => 8, 'max_price' => 17, 'tier_price' => null], ['simple1000' => 8, 'simple1001' => 17] ); } /** * Check bundle prices from index table and final bundle option price. * * @param string $sku * @param array $indexPrices * @param array $expectedPrices * @return void */ private function checkBundlePrices(string $sku, array $indexPrices, array $expectedPrices): void { $product = $this->productRepository->get($sku); $this->assertIndexTableData((int)$product->getId(), $indexPrices); $this->assertPriceWithChosenOption($product, $expectedPrices); } /** * Asserts price data in index table. * * @param int $productId * @param array $expectedPrices * @return void */ private function assertIndexTableData(int $productId, array $expectedPrices): void { $data = $this->getPriceIndexDataByProductId->execute( $productId, Group::NOT_LOGGED_IN_ID, (int)$this->websiteRepository->get('base')->getId() ); $data = reset($data); foreach ($expectedPrices as $column => $price) { $this->assertEquals($price, $data[$column]); } } /** * Assert bundle final price with chosen option. * * @param ProductInterface $bundle * @param array $expectedPrices * @return void */ private function assertPriceWithChosenOption(ProductInterface $bundle, array $expectedPrices): void { $option = $bundle->getExtensionAttributes()->getBundleProductOptions()[0] ?? null; $this->assertNotNull($option); foreach ($option->getProductLinks() as $productLink) { $bundle->addCustomOption('bundle_selection_ids', $this->json->serialize([$productLink->getId()])); $bundle->addCustomOption('selection_qty_' . $productLink->getId(), 1); $this->assertEquals( round((float) $expectedPrices[$productLink->getSku()], 2), round((float) $this->priceModel->getFinalPrice(1, $bundle), 2) ); } } /** * Update products. * * @param array $products * @return void */ private function updateProducts(array $products): void { foreach ($products as $sku => $updateData) { $product = $this->productRepository->get($sku); $product->addData($updateData); $this->productRepository->save($product); } } /** * @return array */ private function specialPricesForOptionsData(): array { return [ 'simple1000' => [ 'special_price' => 8, ], 'simple1001' => [ 'special_price' => 15, ], ]; } /** * @return array */ private function tierPricesForOptionsData(): array { return [ 'simple1000' => [ 'tier_price' => [ [ 'website_id' => 0, 'cust_group' => Group::CUST_GROUP_ALL, 'price_qty' => 1, 'value_type' => TierPriceInterface::PRICE_TYPE_FIXED, 'price' => 8, ], ], ], 'simple1001' => [ 'tier_price' => [ [ 'website_id' => 0, 'cust_group' => Group::CUST_GROUP_ALL, 'price_qty' => 1, 'value_type' => TierPriceInterface::PRICE_TYPE_DISCOUNT, 'website_price' => 20, 'percentage_value' => 15, ], ], ], ]; } }