| Current Path : /home/rtorresani/www/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/ |
| Current File : //home/rtorresani/www/dev/tests/integration/testsuite/Magento/Bundle/Model/Product/PriceTest.php |
<?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,
],
],
],
];
}
}