diff --git a/app/code/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricing.php b/app/code/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricing.php index b763774f917308529f724af8b7758887fe0fd5ba..e37fe022342ed5c194914133b9392ef559ff083a 100644 --- a/app/code/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricing.php +++ b/app/code/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricing.php @@ -76,6 +76,7 @@ class AdvancedPricing extends \Magento\CatalogImportExport\Model\Export\Product ImportAdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => '', ImportAdvancedPricing::COL_TIER_PRICE_QTY => '', ImportAdvancedPricing::COL_TIER_PRICE => '', + ImportAdvancedPricing::COL_TIER_PRICE_TYPE => '' ]; /** @@ -279,6 +280,8 @@ class AdvancedPricing extends \Magento\CatalogImportExport\Model\Export\Product } /** + * Correct export data. + * * @param array $exportData * @return array * @SuppressWarnings(PHPMD.UnusedLocalVariable) @@ -302,6 +305,12 @@ class AdvancedPricing extends \Magento\CatalogImportExport\Model\Export\Product : null ); unset($exportRow[ImportAdvancedPricing::VALUE_ALL_GROUPS]); + } elseif ($keyTemplate === ImportAdvancedPricing::COL_TIER_PRICE) { + $exportRow[$keyTemplate] = $row[ImportAdvancedPricing::COL_TIER_PRICE_PERCENTAGE_VALUE] + ? $row[ImportAdvancedPricing::COL_TIER_PRICE_PERCENTAGE_VALUE] + : $row[ImportAdvancedPricing::COL_TIER_PRICE]; + $exportRow[ImportAdvancedPricing::COL_TIER_PRICE_TYPE] + = $this->tierPriceTypeValue($row[ImportAdvancedPricing::COL_TIER_PRICE_PERCENTAGE_VALUE]); } else { $exportRow[$keyTemplate] = $row[$keyTemplate]; } @@ -311,11 +320,25 @@ class AdvancedPricing extends \Magento\CatalogImportExport\Model\Export\Product $customExportData[$key] = $exportRow; unset($exportRow); } + return $customExportData; } /** - * Get Tier and Group Pricing + * Check type for tier price. + * + * @param string $tierPricePercentage + * @return string + */ + private function tierPriceTypeValue($tierPricePercentage) + { + return $tierPricePercentage + ? ImportAdvancedPricing::TIER_PRICE_TYPE_PERCENT + : ImportAdvancedPricing::TIER_PRICE_TYPE_FIXED; + } + + /** + * Get tier prices. * * @param array $listSku * @param string $table @@ -336,6 +359,7 @@ class AdvancedPricing extends \Magento\CatalogImportExport\Model\Export\Product ImportAdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => 'ap.customer_group_id', ImportAdvancedPricing::COL_TIER_PRICE_QTY => 'ap.qty', ImportAdvancedPricing::COL_TIER_PRICE => 'ap.value', + ImportAdvancedPricing::COL_TIER_PRICE_PERCENTAGE_VALUE => 'ap.percentage_value', ]; if (isset($exportFilter) && !empty($exportFilter)) { $price = $exportFilter['tier_price']; @@ -371,6 +395,9 @@ class AdvancedPricing extends \Magento\CatalogImportExport\Model\Export\Product if (isset($price[1]) && !empty($price[1])) { $select->where('ap.value <= ?', $price[1]); } + if (isset($price[0]) && !empty($price[0]) || isset($price[1]) && !empty($price[1])) { + $select->orWhere('ap.percentage_value IS NOT NULL'); + } if (isset($updatedAtFrom) && !empty($updatedAtFrom)) { $select->where('cpe.updated_at >= ?', $updatedAtFrom); } diff --git a/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing.php b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing.php index 53c4605a819132df0791df60edb325ed5cf5837c..e2275f767ee7ba74ff42528cc6fdb1aa94882c36 100644 --- a/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing.php +++ b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing.php @@ -33,6 +33,14 @@ class AdvancedPricing extends \Magento\ImportExport\Model\Import\Entity\Abstract const COL_TIER_PRICE = 'tier_price'; + const COL_TIER_PRICE_PERCENTAGE_VALUE = 'percentage_value'; + + const COL_TIER_PRICE_TYPE = 'tier_price_value_type'; + + const TIER_PRICE_TYPE_FIXED = 'Fixed'; + + const TIER_PRICE_TYPE_PERCENT = 'Discount'; + const TABLE_TIER_PRICE = 'catalog_product_entity_tier_price'; const DEFAULT_ALL_GROUPS_GROUPED_PRICE_VALUE = '0'; @@ -46,7 +54,7 @@ class AdvancedPricing extends \Magento\ImportExport\Model\Import\Entity\Abstract const VALIDATOR_TEAR_PRICE = 'validator_tear_price'; /** - * Validation failure message template definitions + * Validation failure message template definitions. * * @var array */ @@ -57,6 +65,8 @@ class AdvancedPricing extends \Magento\ImportExport\Model\Import\Entity\Abstract ValidatorInterface::ERROR_INVALID_TIER_PRICE_QTY => 'Tier Price data price or quantity value is invalid', ValidatorInterface::ERROR_INVALID_TIER_PRICE_SITE => 'Tier Price data website is invalid', ValidatorInterface::ERROR_INVALID_TIER_PRICE_GROUP => 'Tier Price customer group is invalid', + ValidatorInterface::ERROR_INVALID_TIER_PRICE_TYPE => 'Value for \'tier_price_value_type\' ' . + 'attribute contains incorrect value, acceptable values are Fixed, Discount', ValidatorInterface::ERROR_TIER_DATA_INCOMPLETE => 'Tier Price data is incomplete', ValidatorInterface::ERROR_INVALID_ATTRIBUTE_DECIMAL => 'Value for \'%s\' attribute contains incorrect value, acceptable values are in decimal format', @@ -70,7 +80,7 @@ class AdvancedPricing extends \Magento\ImportExport\Model\Import\Entity\Abstract protected $needColumnCheck = true; /** - * Valid column names + * Valid column names. * * @array */ @@ -80,6 +90,7 @@ class AdvancedPricing extends \Magento\ImportExport\Model\Import\Entity\Abstract self::COL_TIER_PRICE_CUSTOMER_GROUP, self::COL_TIER_PRICE_QTY, self::COL_TIER_PRICE, + self::COL_TIER_PRICE_TYPE ]; /** @@ -379,7 +390,10 @@ class AdvancedPricing extends \Magento\ImportExport\Model\Import\Entity\Abstract $rowData[self::COL_TIER_PRICE_CUSTOMER_GROUP] ), 'qty' => $rowData[self::COL_TIER_PRICE_QTY], - 'value' => $rowData[self::COL_TIER_PRICE], + 'value' => $rowData[self::COL_TIER_PRICE_TYPE] === self::TIER_PRICE_TYPE_FIXED + ? $rowData[self::COL_TIER_PRICE] : 0, + 'percentage_value' => $rowData[self::COL_TIER_PRICE_TYPE] === self::TIER_PRICE_TYPE_PERCENT + ? $rowData[self::COL_TIER_PRICE] : null, 'website_id' => $this->getWebsiteId($rowData[self::COL_TIER_PRICE_WEBSITE]) ]; } @@ -429,7 +443,7 @@ class AdvancedPricing extends \Magento\ImportExport\Model\Import\Entity\Abstract } } if ($priceIn) { - $this->_connection->insertOnDuplicate($tableName, $priceIn, ['value']); + $this->_connection->insertOnDuplicate($tableName, $priceIn, ['value', 'percentage_value']); } } return $this; diff --git a/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator/TierPrice.php b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator/TierPrice.php index 5bc9bebf420768184c08ea3f2861e5cfcaaf2e21..53ee3013c625beabb7a6f42229a32a9eb7b02eee 100644 --- a/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator/TierPrice.php +++ b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator/TierPrice.php @@ -22,7 +22,8 @@ class TierPrice extends \Magento\CatalogImportExport\Model\Import\Product\Valida AdvancedPricing::COL_TIER_PRICE_WEBSITE, AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP, AdvancedPricing::COL_TIER_PRICE_QTY, - AdvancedPricing::COL_TIER_PRICE + AdvancedPricing::COL_TIER_PRICE, + AdvancedPricing::COL_TIER_PRICE_TYPE ]; /** @@ -101,6 +102,7 @@ class TierPrice extends \Magento\CatalogImportExport\Model\Import\Product\Valida || !isset($value[AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP]) || !isset($value[AdvancedPricing::COL_TIER_PRICE_QTY]) || !isset($value[AdvancedPricing::COL_TIER_PRICE]) + || !isset($value[AdvancedPricing::COL_TIER_PRICE_TYPE]) || $this->hasEmptyColumns($value) ) { $this->_addMessages([self::ERROR_TIER_DATA_INCOMPLETE]); diff --git a/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator/TierPriceType.php b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator/TierPriceType.php new file mode 100644 index 0000000000000000000000000000000000000000..29982459e0211991427aafa5eddecf88011b899d --- /dev/null +++ b/app/code/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricing/Validator/TierPriceType.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing\Validator; + +use Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing; +use Magento\CatalogImportExport\Model\Import\Product\RowValidatorInterface; + +/** + * Class TierPriceType validates tier price type. + */ +class TierPriceType extends \Magento\CatalogImportExport\Model\Import\Product\Validator\AbstractImportValidator +{ + /** + * {@inheritdoc} + */ + public function init($context) + { + return parent::init($context); + } + + /** + * Validate tier price type. + * + * @param array $value + * @return bool + */ + public function isValid($value) + { + $isValid = true; + + if (isset($value[AdvancedPricing::COL_TIER_PRICE_TYPE]) + && !empty($value[AdvancedPricing::COL_TIER_PRICE_TYPE]) + && !in_array( + $value[AdvancedPricing::COL_TIER_PRICE_TYPE], + [AdvancedPricing::TIER_PRICE_TYPE_FIXED, AdvancedPricing::TIER_PRICE_TYPE_PERCENT] + ) + ) { + $this->_addMessages([RowValidatorInterface::ERROR_INVALID_TIER_PRICE_TYPE]); + $isValid = false; + } + + return $isValid; + } +} diff --git a/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricing/Validator/TierPriceTypeTest.php b/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricing/Validator/TierPriceTypeTest.php new file mode 100644 index 0000000000000000000000000000000000000000..6abdd2232fb44233882e140f61f8020aef145289 --- /dev/null +++ b/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricing/Validator/TierPriceTypeTest.php @@ -0,0 +1,78 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\AdvancedPricingImportExport\Test\Unit\Model\Import\AdvancedPricing\Validator; + +use \Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing as AdvancedPricing; + +/** + * Class TierPriceTypeTest. + */ +class TierPriceTypeTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var AdvancedPricing\Validator\TierPriceType + */ + private $tierPriceType; + + /** + * Set up. + * + * @return void + */ + protected function setUp() + { + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->tierPriceType = $objectManager->getObject( + AdvancedPricing\Validator\TierPriceType::class, + [] + ); + } + + /** + * Test for isValid() method. + * + * @dataProvider isValidDataProvider + * @param array $value + * @param bool $expectedResult + */ + public function testIsValid(array $value, $expectedResult) + { + $result = $this->tierPriceType->isValid($value); + $this->assertEquals($expectedResult, $result); + } + + /** + * Data Provider for testIsValid(). + * + * @return array + */ + public function isValidDataProvider() + { + return [ + [ + [AdvancedPricing::COL_TIER_PRICE_TYPE => AdvancedPricing::TIER_PRICE_TYPE_FIXED], + true + ], + [ + [AdvancedPricing::COL_TIER_PRICE_TYPE => AdvancedPricing::TIER_PRICE_TYPE_PERCENT], + true + ], + [ + [], + true + ], + [ + [AdvancedPricing::COL_TIER_PRICE_TYPE => null], + true + ], + [ + [AdvancedPricing::COL_TIER_PRICE_TYPE => 'wrong type'], + false + ] + ]; + } +} diff --git a/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricingTest.php b/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricingTest.php index a289e9970b4999ee82bde14fdb452ef0d78236df..1c80f5789c38108ae9f94030871602f5628086e1 100644 --- a/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricingTest.php +++ b/app/code/Magento/AdvancedPricingImportExport/Test/Unit/Model/Import/AdvancedPricingTest.php @@ -418,33 +418,123 @@ class AdvancedPricingTest extends \Magento\ImportExport\Test\Unit\Model\Import\A $groupWebsiteId, $expectedTierPrices ) { - $this->advancedPricing + $skuProduct = 'product1'; + $sku = $data[0][AdvancedPricing::COL_SKU]; + $advancedPricing = $this->getAdvancedPricingMock( + [ + 'retrieveOldSkus', + 'validateRow', + 'addRowError', + 'getCustomerGroupId', + 'getWebSiteId', + 'deleteProductTierPrices', + 'getBehavior', + 'saveAndReplaceAdvancedPrices', + 'processCountExistingPrices', + 'processCountNewPrices' + ] + ); + $advancedPricing ->expects($this->any()) ->method('getBehavior') ->willReturn(\Magento\ImportExport\Model\Import::BEHAVIOR_APPEND); $this->dataSourceModel->expects($this->at(0))->method('getNextBunch')->willReturn($data); - $this->advancedPricing->expects($this->any())->method('validateRow')->willReturn(true); + $advancedPricing->expects($this->any())->method('validateRow')->willReturn(true); - $this->advancedPricing->expects($this->any())->method('getCustomerGroupId')->willReturnMap( + $advancedPricing->expects($this->any())->method('getCustomerGroupId')->willReturnMap( [ [$data[0][AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP], $tierCustomerGroupId], ] ); - $this->advancedPricing->expects($this->any())->method('getWebSiteId')->willReturnMap( + $advancedPricing->expects($this->any())->method('getWebSiteId')->willReturnMap( [ [$data[0][AdvancedPricing::COL_TIER_PRICE_WEBSITE], $tierWebsiteId], ] ); - $this->advancedPricing->expects($this->any())->method('saveProductPrices')->will($this->returnSelf()); + $oldSkus = [$sku => $skuProduct]; + $expectedTierPrices[$sku][0][self::LINK_FIELD] = $skuProduct; + $advancedPricing->expects($this->once())->method('retrieveOldSkus')->willReturn($oldSkus); + $this->connection->expects($this->once()) + ->method('insertOnDuplicate') + ->with(self::TABLE_NAME, $expectedTierPrices[$sku], ['value', 'percentage_value']); - $this->advancedPricing->expects($this->any())->method('processCountExistingPrices')->willReturnSelf(); - $this->advancedPricing->expects($this->any())->method('processCountNewPrices')->willReturnSelf(); + $advancedPricing->expects($this->any())->method('processCountExistingPrices')->willReturnSelf(); + $advancedPricing->expects($this->any())->method('processCountNewPrices')->willReturnSelf(); - $result = $this->invokeMethod($this->advancedPricing, 'saveAndReplaceAdvancedPrices'); + $result = $this->invokeMethod($advancedPricing, 'saveAndReplaceAdvancedPrices'); - $this->assertEquals($this->advancedPricing, $result); + $this->assertEquals($advancedPricing, $result); + } + + /** + * Test method saveAndReplaceAdvancedPrices with append import behaviour. + */ + public function testSaveAndReplaceAdvancedPricesAppendBehaviourDataAndCallsWithoutTierPrice() + { + $data = [ + 0 => [ + AdvancedPricing::COL_SKU => 'sku value', + AdvancedPricing::COL_TIER_PRICE_WEBSITE => null, + AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => 'tier price customer group value - not all groups', + AdvancedPricing::COL_TIER_PRICE_QTY => 'tier price qty value', + AdvancedPricing::COL_TIER_PRICE => 'tier price value', + AdvancedPricing::COL_TIER_PRICE_TYPE => AdvancedPricing::TIER_PRICE_TYPE_FIXED + ], + ]; + $tierCustomerGroupId = 'tier customer group id value'; + $tierWebsiteId = 'tier website id value'; + $expectedTierPrices = []; + + $skuProduct = 'product1'; + $sku = $data[0][AdvancedPricing::COL_SKU]; + $advancedPricing = $this->getAdvancedPricingMock( + [ + 'retrieveOldSkus', + 'validateRow', + 'addRowError', + 'getCustomerGroupId', + 'getWebSiteId', + 'deleteProductTierPrices', + 'getBehavior', + 'saveAndReplaceAdvancedPrices', + 'processCountExistingPrices', + 'processCountNewPrices' + ] + ); + $advancedPricing + ->expects($this->any()) + ->method('getBehavior') + ->willReturn(\Magento\ImportExport\Model\Import::BEHAVIOR_APPEND); + $this->dataSourceModel->expects($this->at(0))->method('getNextBunch')->willReturn($data); + $advancedPricing->expects($this->any())->method('validateRow')->willReturn(true); + + $advancedPricing->expects($this->any())->method('getCustomerGroupId')->willReturnMap( + [ + [$data[0][AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP], $tierCustomerGroupId], + ] + ); + + $advancedPricing->expects($this->any())->method('getWebSiteId')->willReturnMap( + [ + [$data[0][AdvancedPricing::COL_TIER_PRICE_WEBSITE], $tierWebsiteId], + ] + ); + + $oldSkus = [$sku => $skuProduct]; + $expectedTierPrices[$sku][0][self::LINK_FIELD] = $skuProduct; + $advancedPricing->expects($this->never())->method('retrieveOldSkus')->willReturn($oldSkus); + $this->connection->expects($this->never()) + ->method('insertOnDuplicate') + ->with(self::TABLE_NAME, $expectedTierPrices[$sku], ['value', 'percentage_value']); + + $advancedPricing->expects($this->any())->method('processCountExistingPrices')->willReturnSelf(); + $advancedPricing->expects($this->any())->method('processCountNewPrices')->willReturnSelf(); + + $result = $this->invokeMethod($advancedPricing, 'saveAndReplaceAdvancedPrices'); + + $this->assertEquals($advancedPricing, $result); } /** @@ -575,6 +665,7 @@ class AdvancedPricingTest extends \Magento\ImportExport\Test\Unit\Model\Import\A AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => 'tier price customer group value - not all groups ', AdvancedPricing::COL_TIER_PRICE_QTY => 'tier price qty value', AdvancedPricing::COL_TIER_PRICE => 'tier price value', + AdvancedPricing::COL_TIER_PRICE_TYPE => AdvancedPricing::TIER_PRICE_TYPE_FIXED ], ], '$tierCustomerGroupId' => 'tier customer group id value', @@ -585,25 +676,25 @@ class AdvancedPricingTest extends \Magento\ImportExport\Test\Unit\Model\Import\A 'sku value' => [ [ 'all_groups' => false, - //$rowData[self::COL_TIER_PRICE_CUSTOMER_GROUP] == self::VALUE_ALL_GROUPS 'customer_group_id' => 'tier customer group id value', - //$tierCustomerGroupId 'qty' => 'tier price qty value', 'value' => 'tier price value', 'website_id' => 'tier website id value', + 'percentage_value' => null ], ], ], ], - [// tier customer group is equal to all group + [ '$data' => [ 0 => [ AdvancedPricing::COL_SKU => 'sku value', //tier AdvancedPricing::COL_TIER_PRICE_WEBSITE => 'tier price website value', - AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => AdvancedPricing::VALUE_ALL_GROUPS, + AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => 'tier price customer group value - not all groups ', AdvancedPricing::COL_TIER_PRICE_QTY => 'tier price qty value', AdvancedPricing::COL_TIER_PRICE => 'tier price value', + AdvancedPricing::COL_TIER_PRICE_TYPE => AdvancedPricing::TIER_PRICE_TYPE_PERCENT ], ], '$tierCustomerGroupId' => 'tier customer group id value', @@ -613,33 +704,44 @@ class AdvancedPricingTest extends \Magento\ImportExport\Test\Unit\Model\Import\A '$expectedTierPrices' => [ 'sku value' => [ [ - 'all_groups' => true, - //$rowData[self::COL_TIER_PRICE_CUSTOMER_GROUP] == self::VALUE_ALL_GROUPS + 'all_groups' => false, 'customer_group_id' => 'tier customer group id value', - //$tierCustomerGroupId 'qty' => 'tier price qty value', - 'value' => 'tier price value', + 'value' => 0, + 'percentage_value' => 'tier price value', 'website_id' => 'tier website id value', ], ], ], ], - [ + [// tier customer group is equal to all group '$data' => [ 0 => [ AdvancedPricing::COL_SKU => 'sku value', //tier - AdvancedPricing::COL_TIER_PRICE_WEBSITE => null, - AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => 'tier price customer group value - not all groups', + AdvancedPricing::COL_TIER_PRICE_WEBSITE => 'tier price website value', + AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => AdvancedPricing::VALUE_ALL_GROUPS, AdvancedPricing::COL_TIER_PRICE_QTY => 'tier price qty value', AdvancedPricing::COL_TIER_PRICE => 'tier price value', + AdvancedPricing::COL_TIER_PRICE_TYPE => AdvancedPricing::TIER_PRICE_TYPE_FIXED ], ], '$tierCustomerGroupId' => 'tier customer group id value', '$groupCustomerGroupId' => 'group customer group id value', '$tierWebsiteId' => 'tier website id value', '$groupWebsiteId' => 'group website id value', - '$expectedTierPrices' => [], + '$expectedTierPrices' => [ + 'sku value' => [ + [ + 'all_groups' => true, + 'customer_group_id' => 'tier customer group id value', + 'qty' => 'tier price qty value', + 'value' => 'tier price value', + 'website_id' => 'tier website id value', + 'percentage_value' => null + ], + ], + ], ], [ '$data' => [ @@ -650,6 +752,7 @@ class AdvancedPricingTest extends \Magento\ImportExport\Test\Unit\Model\Import\A AdvancedPricing::COL_TIER_PRICE_CUSTOMER_GROUP => 'tier price customer group value - not all groups', AdvancedPricing::COL_TIER_PRICE_QTY => 'tier price qty value', AdvancedPricing::COL_TIER_PRICE => 'tier price value', + AdvancedPricing::COL_TIER_PRICE_TYPE => AdvancedPricing::TIER_PRICE_TYPE_FIXED ], ], '$tierCustomerGroupId' => 'tier customer group id value', @@ -660,12 +763,11 @@ class AdvancedPricingTest extends \Magento\ImportExport\Test\Unit\Model\Import\A 'sku value' => [ [ 'all_groups' => false, - //$rowData[self::COL_TIER_PRICE_CUSTOMER_GROUP] == self::VALUE_ALL_GROUPS 'customer_group_id' => 'tier customer group id value', - //$tierCustomerGroupId 'qty' => 'tier price qty value', 'value' => 'tier price value', 'website_id' => 'tier website id value', + 'percentage_value' => null ], ] ], @@ -746,7 +848,7 @@ class AdvancedPricingTest extends \Magento\ImportExport\Test\Unit\Model\Import\A $this->connection->expects($this->exactly($callNum)) ->method('insertOnDuplicate') - ->with(self::TABLE_NAME, $priceIn, ['value']); + ->with(self::TABLE_NAME, $priceIn, ['value', 'percentage_value']); $this->invokeMethod($this->advancedPricing, 'saveProductPrices', [$priceData, 'table']); } diff --git a/app/code/Magento/AdvancedPricingImportExport/etc/di.xml b/app/code/Magento/AdvancedPricingImportExport/etc/di.xml index c3444069c14c0f3f226bad8cf61ef0d5fc092c17..711d4b8b399f5a8709a1fa0760b16c1aad60106f 100644 --- a/app/code/Magento/AdvancedPricingImportExport/etc/di.xml +++ b/app/code/Magento/AdvancedPricingImportExport/etc/di.xml @@ -14,6 +14,7 @@ <argument name="validators" xsi:type="array"> <item name="tierPrice" xsi:type="object">Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing\Validator\TierPrice</item> <item name="website" xsi:type="object">Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing\Validator\Website</item> + <item name="tierPriceType" xsi:type="object">Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing\Validator\TierPriceType</item> </argument> </arguments> </type> diff --git a/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/TierPriceTest.php b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/TierPriceTest.php new file mode 100644 index 0000000000000000000000000000000000000000..9dccb8eef452ad50e58e04aef1f7273a3fb524a4 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Ui/DataProvider/Product/Form/Modifier/TierPriceTest.php @@ -0,0 +1,141 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Test\Unit\Ui\DataProvider\Product\Form\Modifier; + +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Model\Config\Source\ProductPriceOptionsInterface; +use Magento\Framework\Stdlib\ArrayManager; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\TierPrice; + +/** + * Class TierPriceTest. + */ +class TierPriceTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var ProductPriceOptionsInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $productPriceOptions; + + /** + * @var ArrayManager|\PHPUnit_Framework_MockObject_MockObject + */ + private $arrayManager; + + /** + * @var TierPrice + */ + private $tierPrice; + + /** + * Set Up. + * @return void + */ + protected function setUp() + { + $this->productPriceOptions = $this->getMock(ProductPriceOptionsInterface::class); + $this->arrayManager = $this->getMock(ArrayManager::class, [], [], '', false); + + $this->tierPrice = (new ObjectManager($this))->getObject(TierPrice::class, [ + 'productPriceOptions' => $this->productPriceOptions, + 'arrayManager' => $this->arrayManager, + ]); + } + + /** + * Test modifyData. + */ + public function testModifyData() + { + $data = [1, 2]; + $this->assertEquals($data, $this->tierPrice->modifyData($data)); + } + + /** + * Test modifyMeta. + */ + public function testModifyMeta() + { + $meta = [1, 2]; + $tierPricePath = 'tier_price'; + $priceWrapperPath = 'tier_price/some-wrapper'; + $pricePath = $priceWrapperPath . '/price'; + $priceMeta = [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'visible' => true, + 'validation' => ['validate-number' => true], + ], + ], + ], + ]; + + $this->productPriceOptions->expects($this->once())->method('toOptionArray')->willReturn([ + [ + 'value' => ProductPriceOptionsInterface::VALUE_FIXED, + 'label' => 'label1', + ], + ]); + + $this->productPriceOptions->expects($this->once())->method('toOptionArray')->willReturn([ + [ + 'value' => ProductPriceOptionsInterface::VALUE_FIXED, + 'label' => 'label1', + ], + ]); + + $this->arrayManager + ->expects($this->exactly(2)) + ->method('findPath') + ->willReturnMap([ + [ + ProductAttributeInterface::CODE_TIER_PRICE, + $meta, + null, + 'children', + ArrayManager::DEFAULT_PATH_DELIMITER, + $tierPricePath + ], + [ + ProductAttributeInterface::CODE_TIER_PRICE_FIELD_PRICE, + $meta, + $tierPricePath, + null, + ArrayManager::DEFAULT_PATH_DELIMITER, + $pricePath + ], + ]); + $this->arrayManager + ->expects($this->once()) + ->method('get') + ->with($pricePath, $meta) + ->willReturn($priceMeta); + $this->arrayManager + ->expects($this->once()) + ->method('remove') + ->with($pricePath, $meta) + ->willReturn($meta); + $this->arrayManager + ->expects($this->once()) + ->method('slicePath') + ->with($pricePath, 0, -1) + ->willReturn($priceWrapperPath); + $this->arrayManager + ->expects($this->once()) + ->method('merge') + ->with($priceWrapperPath, $meta, $this->isType('array')) + ->willReturnArgument(2); + + $modifiedMeta = $this->tierPrice->modifyMeta($meta); + $children = $modifiedMeta['price_value']['children']; + + $this->assertNotEmpty($children[ProductAttributeInterface::CODE_TIER_PRICE_FIELD_VALUE_TYPE]); + $this->assertNotEmpty($children[ProductAttributeInterface::CODE_TIER_PRICE_FIELD_PERCENTAGE_VALUE]); + $this->assertEquals($priceMeta, $children[ProductAttributeInterface::CODE_TIER_PRICE_FIELD_PRICE]); + } +} diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php new file mode 100644 index 0000000000000000000000000000000000000000..ac511edcf2e5338eba6b83c60cf38a749406cf89 --- /dev/null +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/TierPrice.php @@ -0,0 +1,171 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Catalog\Ui\DataProvider\Product\Form\Modifier; + +use Magento\Catalog\Api\Data\ProductAttributeInterface; +use Magento\Catalog\Model\Config\Source\ProductPriceOptionsInterface; +use Magento\Framework\Stdlib\ArrayManager; +use Magento\Ui\Component\Container; +use Magento\Ui\Component\Form\Element\DataType\Price; +use Magento\Ui\Component\Form\Element\Input; +use Magento\Ui\Component\Form\Element\Select; +use Magento\Ui\Component\Form\Field; + +/** + * Tier prices modifier adds price type option to tier prices. + */ +class TierPrice extends AbstractModifier +{ + /** + * @var ProductPriceOptionsInterface + */ + private $productPriceOptions; + + /** + * @var ArrayManager + */ + private $arrayManager; + + /** + * @param ProductPriceOptionsInterface $productPriceOptions + * @param ArrayManager $arrayManager + */ + public function __construct( + ProductPriceOptionsInterface $productPriceOptions, + ArrayManager $arrayManager + ) { + $this->productPriceOptions = $productPriceOptions; + $this->arrayManager = $arrayManager; + } + + /** + * {@inheritdoc} + */ + public function modifyData(array $data) + { + return $data; + } + + /** + * {@inheritdoc} + */ + public function modifyMeta(array $meta) + { + $tierPricePath = $this->arrayManager->findPath( + ProductAttributeInterface::CODE_TIER_PRICE, + $meta, + null, + 'children' + ); + if ($tierPricePath) { + $pricePath = $this->arrayManager->findPath( + ProductAttributeInterface::CODE_TIER_PRICE_FIELD_PRICE, + $meta, + $tierPricePath + ); + + if ($pricePath) { + $priceMeta = $this->arrayManager->get($pricePath, $meta); + $updatedStructure = $this->getUpdatedTierPriceStructure($priceMeta); + $meta = $this->arrayManager->remove($pricePath, $meta); + $meta = $this->arrayManager->merge( + $this->arrayManager->slicePath($pricePath, 0, -1), + $meta, + $updatedStructure + ); + } + } + return $meta; + } + + /** + * Get updated tier price structure. + * + * @param array $priceMeta + * @return array + */ + private function getUpdatedTierPriceStructure(array $priceMeta) + { + $priceTypeOptions = $this->productPriceOptions->toOptionArray(); + $firstOption = $priceTypeOptions ? current($priceTypeOptions) : null; + + $priceMeta['arguments']['data']['config']['visible'] = $firstOption + && $firstOption['value'] == ProductPriceOptionsInterface::VALUE_FIXED; + $priceMeta['arguments']['data']['config']['validation'] = [ + 'validate-number' => true, + ]; + return [ + 'price_value' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'componentType' => Container::NAME, + 'formElement' => Container::NAME, + 'dataType' => Price::NAME, + 'component' => 'Magento_Ui/js/form/components/group', + 'label' => __('Price'), + 'enableLabel' => true, + 'dataScope' => '', + 'sortOrder' => isset($priceMeta['arguments']['data']['config']['sortOrder']) + ? $priceMeta['arguments']['data']['config']['sortOrder'] : 40, + ], + ], + ], + 'children' => [ + ProductAttributeInterface::CODE_TIER_PRICE_FIELD_VALUE_TYPE => [ + 'arguments' => [ + 'data' => [ + 'options' => $priceTypeOptions, + 'config' => [ + 'componentType' => Field::NAME, + 'formElement' => Select::NAME, + 'dataType' => 'text', + 'component' => 'Magento_Catalog/js/tier-price/value-type-select', + 'prices' => [ + ProductPriceOptionsInterface::VALUE_FIXED => '${ $.parentName }.' + . ProductAttributeInterface::CODE_TIER_PRICE_FIELD_PRICE, + ProductPriceOptionsInterface::VALUE_PERCENT => '${ $.parentName }.' + . ProductAttributeInterface::CODE_TIER_PRICE_FIELD_PERCENTAGE_VALUE, + ], + ], + ], + ], + ], + ProductAttributeInterface::CODE_TIER_PRICE_FIELD_PRICE => $priceMeta, + ProductAttributeInterface::CODE_TIER_PRICE_FIELD_PERCENTAGE_VALUE => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'componentType' => Field::NAME, + 'formElement' => Input::NAME, + 'dataType' => Price::NAME, + 'addbefore' => '%', + 'validation' => [ + 'validate-number' => true, + 'less-than-equals-to' => 100 + ], + 'visible' => $firstOption + && $firstOption['value'] == ProductPriceOptionsInterface::VALUE_PERCENT, + ], + ], + ], + ], + 'price_calc' => [ + 'arguments' => [ + 'data' => [ + 'config' => [ + 'componentType' => Container::NAME, + 'component' => 'Magento_Catalog/js/tier-price/percentage-processor', + 'visible' => false + ], + ], + ] + ] + ], + ], + ]; + } +} diff --git a/app/code/Magento/Catalog/etc/adminhtml/di.xml b/app/code/Magento/Catalog/etc/adminhtml/di.xml index 584a2e51514db53a0250615a55d67455ca1a0f57..5fc5ec8d44fd0a88f8a5d542d5518fa60ad77629 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/di.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/di.xml @@ -142,6 +142,10 @@ <item name="class" xsi:type="string">Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Attributes</item> <item name="sortOrder" xsi:type="number">120</item> </item> + <item name="advanced-pricing-tier-price-type" xsi:type="array"> + <item name="class" xsi:type="string">Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\TierPrice</item> + <item name="sortOrder" xsi:type="number">150</item> + </item> </argument> </arguments> </virtualType> diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/tier-price/percentage-processor.js b/app/code/Magento/Catalog/view/adminhtml/web/js/tier-price/percentage-processor.js new file mode 100644 index 0000000000000000000000000000000000000000..afc0bab3f7fa4baa4dfbb76dc261b0e97672d80e --- /dev/null +++ b/app/code/Magento/Catalog/view/adminhtml/web/js/tier-price/percentage-processor.js @@ -0,0 +1,73 @@ +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'uiElement', + 'underscore', + 'Magento_Ui/js/lib/view/utils/async', + 'Magento_Catalog/js/utils/percentage-price-calculator' +], function (Element, _, $, percentagePriceCalculator) { + 'use strict'; + + return Element.extend({ + defaults: { + priceElem: '${ $.parentName }.price', + selector: 'input', + imports: { + priceValue: '${ $.priceElem }:priceValue' + }, + exports: { + calculatedVal: '${ $.priceElem }:value' + } + }, + + /** + * {@inheritdoc} + */ + initialize: function () { + this._super(); + + _.bindAll(this, 'initPriceListener', 'onInput'); + + $.async({ + component: this.priceElem, + selector: this.selector + }, this.initPriceListener); + + return this; + }, + + /** + * {@inheritdoc} + */ + initObservable: function () { + return this._super() + .observe(['visible']); + }, + + /** + * Handles keyup event on price input. + * + * {@param} HTMLElement elem + */ + initPriceListener: function (elem) { + $(elem).on('keyup.priceCalc', this.onInput); + }, + + /** + * Delegates calculation of the price input value to percentagePriceCalculator. + * + * {@param} object event + */ + onInput: function (event) { + var value = event.currentTarget.value; + + if (value.slice(-1) === '%') { + value = percentagePriceCalculator(this.priceValue, value); + this.set('calculatedVal', value); + } + } + }); +}); diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/tier-price/value-type-select.js b/app/code/Magento/Catalog/view/adminhtml/web/js/tier-price/value-type-select.js new file mode 100644 index 0000000000000000000000000000000000000000..216a767d393d57e6596c4d9d75cb7aef0e4a86f7 --- /dev/null +++ b/app/code/Magento/Catalog/view/adminhtml/web/js/tier-price/value-type-select.js @@ -0,0 +1,118 @@ +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +define([ + 'Magento_Ui/js/form/element/select', + 'uiRegistry', + 'underscore' +], function (Select, uiRegistry, _) { + 'use strict'; + + return Select.extend({ + defaults: { + prices: {} + }, + + /** + * {@inheritdoc} + */ + initialize: function () { + this._super() + .prepareForm(); + }, + + /** + * {@inheritdoc} + */ + setInitialValue: function () { + this.initialValue = this.getInitialValue(); + + if (this.value.peek() !== this.initialValue) { + this.value(this.initialValue); + } + + this.isUseDefault(this.disabled()); + + return this; + }, + + /** + * {@inheritdoc} + */ + prepareForm: function () { + var elements = this.getElementsByPrices(), + prices = this.prices, + currencyType = _.keys(prices)[0], + select = this; + + uiRegistry.get(elements, function () { + _.each(arguments, function (currentValue) { + if (parseFloat(currentValue.value()) > 0) { + _.each(prices, function (priceValue, priceKey) { + if (priceValue === currentValue.name) { + currencyType = priceKey; + } + }); + } + }); + select.value(currencyType); + select.on('value', select.onUpdate.bind(select)); + select.onUpdate(); + }); + }, + + /** + * @returns {Array} + */ + getElementsByPrices: function () { + var elements = []; + + _.each(this.prices, function (currentValue) { + elements.push(currentValue); + }); + + return elements; + }, + + /** + * Callback that fires when 'value' property is updated + */ + onUpdate: function () { + var value = this.value(), + prices = this.prices, + select = this, + parentDataScopeArr = this.dataScope.split('.'), + parentDataScope, + elements = this.getElementsByPrices(); + + parentDataScopeArr.pop(); + parentDataScope = parentDataScopeArr.join('.'); + + uiRegistry.get(elements, function () { + var sourceData = select.source.get(parentDataScope); + + _.each(arguments, function (currentElement) { + var index; + + _.each(prices, function (priceValue, priceKey) { + if (priceValue === currentElement.name) { + index = priceKey; + } + }); + + if (value === index) { + currentElement.visible(true); + sourceData[currentElement.index] = currentElement.value(); + } else { + currentElement.value(''); + currentElement.visible(false); + delete sourceData[currentElement.index]; + } + }); + select.source.set(parentDataScope, sourceData); + }); + } + }); +}); diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/utils/percentage-price-calculator.js b/app/code/Magento/Catalog/view/adminhtml/web/js/utils/percentage-price-calculator.js new file mode 100644 index 0000000000000000000000000000000000000000..b7c18d5332f56347d269dc623ff05c2bff936959 --- /dev/null +++ b/app/code/Magento/Catalog/view/adminhtml/web/js/utils/percentage-price-calculator.js @@ -0,0 +1,45 @@ +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +define(['Magento_Ui/js/lib/validation/utils'], function (utils) { + 'use strict'; + + /** + * Calculates the price input value when entered percentage value. + * + * @param {String} price + * @param {String} input + * + * @returns {String} + */ + return function (price, input) { + var result, + lastInputSymbol = input.slice(-1), + inputPercent = input.slice(0, -1), + parsedPercent = utils.parseNumber(inputPercent), + parsedPrice = utils.parseNumber(price); + + if (lastInputSymbol !== '%') { + result = input; + } else if ( + input === '%' || + isNaN(parsedPrice) || + parsedPercent != inputPercent || /* eslint eqeqeq:0 */ + isNaN(parsedPercent) || + input === '' + ) { + result = ''; + } else if (parsedPercent > 100) { + result = '0.00'; + } else if (lastInputSymbol === '%') { + result = parsedPrice - parsedPrice * (inputPercent / 100); + result = result.toFixed(2); + } else { + result = input; + } + + return result; + }; +}); diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product/RowValidatorInterface.php b/app/code/Magento/CatalogImportExport/Model/Import/Product/RowValidatorInterface.php index 45cd01bc8252d71711389ae3a837cffb2a846ac0..3f9e157c5710a63c1bb3a47d0462953e96ac48c1 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product/RowValidatorInterface.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product/RowValidatorInterface.php @@ -45,6 +45,8 @@ interface RowValidatorInterface extends \Magento\Framework\Validator\ValidatorIn const ERROR_INVALID_TIER_PRICE_GROUP = 'tierPriceGroupInvalid'; + const ERROR_INVALID_TIER_PRICE_TYPE = 'tierPriceTypeInvalid'; + const ERROR_TIER_DATA_INCOMPLETE = 'tierPriceDataIsIncomplete'; const ERROR_SKU_NOT_FOUND_FOR_DELETE = 'skuNotFoundToDelete'; diff --git a/app/code/Magento/ImportExport/Block/Adminhtml/Export/Filter.php b/app/code/Magento/ImportExport/Block/Adminhtml/Export/Filter.php index 2f054b5a9c4d10c93dd83193493ae35cd4f00a48..6d3f8b763026cd0514b6c87b4dc898717bc75ee0 100644 --- a/app/code/Magento/ImportExport/Block/Adminhtml/Export/Filter.php +++ b/app/code/Magento/ImportExport/Block/Adminhtml/Export/Filter.php @@ -6,6 +6,7 @@ namespace Magento\ImportExport\Block\Adminhtml\Export; use Magento\Eav\Model\Entity\Attribute; +use Magento\Catalog\Api\Data\ProductAttributeInterface; /** * Export filter block @@ -185,25 +186,39 @@ class Filter extends \Magento\Backend\Block\Widget\Grid\Extended $toValue = $this->escapeHtml(next($value)); } - return '<strong class="admin__control-support-text">' . __( - 'From' - ) . - ':</strong> ' . - '<input type="text" name="' . - $name . - '[]" class="admin__control-text input-text input-text-range"' . - ' value="' . - $fromValue . - '"/> ' . - '<strong class="admin__control-support-text">' . - __( - 'To' - ) . - ':</strong> <input type="text" name="' . - $name . - '[]" class="admin__control-text input-text input-text-range" value="' . - $toValue . - '" />'; + return '<strong class="admin__control-support-text">' . + $this->getFromAttributePrefix($attribute) . + ':</strong> ' . + '<input type="text" name="' . + $name . + '[]" class="admin__control-text input-text input-text-range"' . + ' value="' . + $fromValue . + '"/> ' . + '<strong class="admin__control-support-text">' . + __( + 'To' + ) . + ':</strong> <input type="text" name="' . + $name . + '[]" class="admin__control-text input-text input-text-range" value="' . + $toValue . + '" />'; + } + + /** + * Get 'From' prefix to attribute. + * + * @param Attribute $attribute + * @return \Magento\Framework\Phrase + */ + protected function getFromAttributePrefix(Attribute $attribute) + { + $attributePrefix = $attribute->getAttributeCode() === ProductAttributeInterface::CODE_TIER_PRICE + ? __('Fixed Price: From') + : __('From'); + + return $attributePrefix; } /** diff --git a/app/code/Magento/ImportExport/Files/Sample/advanced_pricing.csv b/app/code/Magento/ImportExport/Files/Sample/advanced_pricing.csv index 82db6382bd8d5fda511e50a6da9fa4876fa35f1a..e4c203bed3be9ae6f935e7cb1f6cfbeb086d8816 100644 --- a/app/code/Magento/ImportExport/Files/Sample/advanced_pricing.csv +++ b/app/code/Magento/ImportExport/Files/Sample/advanced_pricing.csv @@ -1,5 +1,5 @@ -sku,tier_price_website,tier_price_customer_group,tier_price_qty,tier_price -sku123,base,General,2,10 -sku124,All Websites [USD],ALL GROUPS,3,15 -sku123,,,, -sku124,,,, +sku,tier_price_website,tier_price_customer_group,tier_price_qty,tier_price,tier_price_value_type +sku123,base,General,2,10,Fixed +sku124,All Websites [USD],ALL GROUPS,3,15,Discount +sku123,,,,, +sku124,,,,, diff --git a/app/code/Magento/ImportExport/composer.json b/app/code/Magento/ImportExport/composer.json index 5e403d2180652a6f3565bf03b573abf75310f19f..d5e3b41f47fd93387b0ce70e1764105fa7341027 100644 --- a/app/code/Magento/ImportExport/composer.json +++ b/app/code/Magento/ImportExport/composer.json @@ -3,6 +3,7 @@ "description": "N/A", "require": { "php": "~5.6.5|7.0.2|7.0.4|~7.0.6", + "magento/module-catalog": "101.1.*", "magento/module-store": "100.2.*", "magento/module-backend": "100.2.*", "magento/module-eav": "100.2.*", diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Section/AdvancedPricing/OptionTier.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Section/AdvancedPricing/OptionTier.xml index ca10de4ebf8c127fdb310073437261cab7d7a435..9df266994f4fad767a0c13478bdc2e8cbb002c99 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Section/AdvancedPricing/OptionTier.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Adminhtml/Product/Edit/Section/AdvancedPricing/OptionTier.xml @@ -10,6 +10,13 @@ <price> <selector>[name$="[price]"]</selector> </price> + <value_type> + <selector>[name$="[value_type]"]</selector> + <input>select</input> + </value_type> + <percentage_value> + <selector>[name$="[percentage_value]"]</selector> + </percentage_value> <website> <selector>[name$="[website_id]"]</selector> <input>select</input> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductTierPriceInCart.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductTierPriceInCart.php new file mode 100644 index 0000000000000000000000000000000000000000..1bc689d8e1b1e3009bbf2f3fc563b3aa51ccc3c7 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductTierPriceInCart.php @@ -0,0 +1,129 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Catalog\Test\Constraint; + +use Magento\Catalog\Test\Fixture\CatalogProductSimple; +use Magento\Catalog\Test\Page\Product\CatalogProductView; +use Magento\Checkout\Test\Page\CheckoutCart; +use Magento\Mtf\Client\BrowserInterface; +use Magento\Mtf\Constraint\AbstractConstraint; +use Magento\Mtf\Fixture\FixtureInterface; + +/** + * This assert adds product to cart and checks price. + */ +class AssertProductTierPriceInCart extends AbstractConstraint +{ + /** + * Price on form. + * + * @var string + */ + private $formPrice; + + /** + * Fixture actual price. + * + * @var string + */ + private $fixtureActualPrice; + + /** + * Fixture price. + * + * @var string + */ + private $fixturePrice; + + /** + * Assertion that the product is correctly displayed in cart. + * + * @param CatalogProductView $catalogProductView + * @param FixtureInterface $product + * @param BrowserInterface $browser + * @param CheckoutCart $checkoutCart + * @return void + */ + public function processAssert( + CatalogProductView $catalogProductView, + FixtureInterface $product, + BrowserInterface $browser, + CheckoutCart $checkoutCart + ) { + $checkoutCart->open(); + $checkoutCart->getCartBlock()->clearShoppingCart(); + // Add product to cart + $browser->open($_ENV['app_frontend_url'] . $product->getUrlKey() . '.html'); + $requiredQty = $product->getDataFieldConfig('tier_price')['source']->getData()[0]['price_qty']; + $catalogProductView->getViewBlock()->setQtyAndClickAddToCart($requiredQty); + $catalogProductView->getMessagesBlock()->waitSuccessMessage(); + + // Check price + $this->countPrices($product, $checkoutCart); + \PHPUnit_Framework_Assert::assertEquals( + $this->fixtureActualPrice, + $this->formPrice, + 'Product price in shopping cart is not correct.' + ); + } + + /** + * Count prices. + * + * @param FixtureInterface $product + * @param CheckoutCart $checkoutCart + * @return void + */ + private function countPrices(FixtureInterface $product, CheckoutCart $checkoutCart) + { + /** @var CatalogProductSimple $product */ + $this->fixturePrice = $product->getPrice(); + $this->prepareFormPrice($product, $checkoutCart); + $this->countCheckoutCartItemPrice($product); + } + + /** + * Prepare form price. + * + * @param FixtureInterface $product + * @param CheckoutCart $checkoutCart + * @return void + */ + private function prepareFormPrice(FixtureInterface $product, CheckoutCart $checkoutCart) + { + $checkoutCart->open(); + $cartItem = $checkoutCart->getCartBlock()->getCartItem($product); + $this->formPrice = $cartItem->getPrice(); + } + + /** + * Count cart item price. + * + * @param FixtureInterface $product + * @return void + */ + private function countCheckoutCartItemPrice(FixtureInterface $product) + { + $tierPrice = $product->getDataFieldConfig('tier_price')['source']->getData()[0]; + + if ($tierPrice['value_type'] === "Percent") { + $this->fixtureActualPrice = $this->fixturePrice * (1 - $tierPrice['percentage_value'] / 100); + } else { + $this->fixtureActualPrice = $tierPrice['price']; + } + } + + /** + * Returns a string representation of the object. + * + * @return string + */ + public function toString() + { + return 'Product is correctly displayed in cart.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Product/TierPrice.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Product/TierPrice.xml index 412818cbe40e71634eb1e635d8dc8e1630913b49..2f517f4d6cf3cc03020abc575d4e91cd7f7188da 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Product/TierPrice.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Repository/Product/TierPrice.xml @@ -77,5 +77,29 @@ </item> </field> </dataset> + + <dataset name="custom_with_fixed_discount"> + <field name="0" xsi:type="array"> + <item name="value_type" xsi:type="string">Fixed</item> + <item name="price" xsi:type="string">95</item> + <item name="website" xsi:type="string">All Websites [USD]</item> + <item name="price_qty" xsi:type="string">5</item> + <item name="customer_group" xsi:type="array"> + <item name="dataset" xsi:type="string">ALL_GROUPS</item> + </item> + </field> + </dataset> + + <dataset name="custom_with_percentage_discount"> + <field name="0" xsi:type="array"> + <item name="value_type" xsi:type="string">Percent</item> + <item name="percentage_value" xsi:type="string">3</item> + <item name="website" xsi:type="string">All Websites [USD]</item> + <item name="price_qty" xsi:type="string">10</item> + <item name="customer_group" xsi:type="array"> + <item name="dataset" xsi:type="string">ALL_GROUPS</item> + </item> + </field> + </dataset> </repository> </config> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateSimpleProductEntityTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateSimpleProductEntityTest.xml index 9f4a5e4bb71fea9293612125157757f7e3d965da..ee168a9baa0be4753cbfafb0a470957ebbcc6f0c 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateSimpleProductEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateSimpleProductEntityTest.xml @@ -509,6 +509,24 @@ <constraint name="Magento\Catalog\Test\Constraint\AssertProductPage" /> <constraint name="Magento\Catalog\Test\Constraint\AssertProductInCart" /> </variation> + <variation name="CreateSimpleProductEntityWithTierPriceTestVariation1" summary="Create Simple Product with fixed tier price."> + <data name="product/data/url_key" xsi:type="string">simple-product-%isolation%</data> + <data name="product/data/name" xsi:type="string">Simple Product %isolation%</data> + <data name="product/data/sku" xsi:type="string">simple_sku_%isolation%</data> + <data name="product/data/price/value" xsi:type="string">100.00</data> + <data name="product/data/quantity_and_stock_status/qty" xsi:type="string">555</data> + <data name="product/data/tier_price/dataset" xsi:type="string">custom_with_fixed_discount</data> + <constraint name="Magento\Catalog\Test\Constraint\AssertProductTierPriceInCart" /> + </variation> + <variation name="CreateSimpleProductEntityWithTierPriceTestVariation2" summary="Create Simple Product with percentage tier price."> + <data name="product/data/url_key" xsi:type="string">simple-product-%isolation%</data> + <data name="product/data/name" xsi:type="string">Simple Product %isolation%</data> + <data name="product/data/sku" xsi:type="string">simple_sku_%isolation%</data> + <data name="product/data/price/value" xsi:type="string">200.00</data> + <data name="product/data/quantity_and_stock_status/qty" xsi:type="string">555</data> + <data name="product/data/tier_price/dataset" xsi:type="string">custom_with_percentage_discount</data> + <constraint name="Magento\Catalog\Test\Constraint\AssertProductTierPriceInCart" /> + </variation> <variation name="CreateSimpleProductEntityWithImageTestVariation1" summary="Create product with image and try to save it again"> <data name="product/data/image/0/file" xsi:type="string">Magento/Catalog/Test/_files/test1.png</data> <data name="product/data/image/1/file" xsi:type="string">Magento/Catalog/Test/_files/test2.png</data> diff --git a/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricingTest.php b/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricingTest.php index b8c72e9a3ebb19cdabc75933d56c0566e3f71bb8..4226ec190020cb4670c693ca681193b09ab30884 100644 --- a/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricingTest.php +++ b/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricingTest.php @@ -59,7 +59,9 @@ class AdvancedPricingTest extends \PHPUnit_Framework_TestCase $csvfile = uniqid('importexport_') . '.csv'; - $this->exportData($csvfile); + $exportContent = $this->exportData($csvfile); + $this->assertDiscountTypes($exportContent); + $this->importData($csvfile); while ($index > 0) { @@ -72,6 +74,24 @@ class AdvancedPricingTest extends \PHPUnit_Framework_TestCase } } + /** + * Assert for correct tier prices discount types. + * + * @param string $exportContent + * @return void + */ + private function assertDiscountTypes($exportContent) + { + $this->assertContains( + '2.0000,8.0000,Fixed', + $exportContent + ); + $this->assertContains( + '10.0000,50.00,Discount', + $exportContent + ); + } + /** * @magentoAppArea adminhtml * @magentoDbIsolation enabled diff --git a/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricingTest.php b/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricingTest.php index e5fd4cc0d9630080eb7c06f2558b02be889f8a57..bdbd06e2d626630c993ac72052d1119b5e323fbf 100644 --- a/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricingTest.php +++ b/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Import/AdvancedPricingTest.php @@ -48,34 +48,52 @@ class AdvancedPricingTest extends \PHPUnit_Framework_TestCase [ 'customer_group_id' => \Magento\Customer\Model\Group::CUST_GROUP_ALL, 'value' => '300.0000', - 'qty' => '10.0000' + 'qty' => '10.0000', + 'percentage_value' => null ], [ 'customer_group_id' => '1', 'value' => '11.0000', - 'qty' => '11.0000' + 'qty' => '11.0000', + 'percentage_value' => null ], [ 'customer_group_id' => '3', 'value' => '14.0000', - 'qty' => '14.0000' + 'qty' => '14.0000', + 'percentage_value' => null + ], + [ + 'customer_group_id' => \Magento\Customer\Model\Group::CUST_GROUP_ALL, + 'value' => '160.5000', + 'qty' => '20.0000', + 'percentage_value' => '50.0000' ] ], 'AdvancedPricingSimple 2' => [ [ 'customer_group_id' => \Magento\Customer\Model\Group::CUST_GROUP_ALL, 'value' => '1000000.0000', - 'qty' => '100.0000' + 'qty' => '100.0000', + 'percentage_value' => null ], [ 'customer_group_id' => '0', 'value' => '12.0000', - 'qty' => '12.0000' + 'qty' => '12.0000', + 'percentage_value' => null ], [ 'customer_group_id' => '2', 'value' => '13.0000', - 'qty' => '13.0000' + 'qty' => '13.0000', + 'percentage_value' => null + ], + [ + 'customer_group_id' => \Magento\Customer\Model\Group::CUST_GROUP_ALL, + 'value' => '327.0000', + 'qty' => '200.0000', + 'percentage_value' => '50.0000' ] ] ]; @@ -121,18 +139,40 @@ class AdvancedPricingTest extends \PHPUnit_Framework_TestCase foreach ($productIdList as $sku => $productId) { $product->load($productId); $tierPriceCollection = $product->getTierPrices(); - $this->assertEquals(3, count($tierPriceCollection)); + $this->assertEquals(4, count($tierPriceCollection)); + $index = 0; /** @var \Magento\Catalog\Model\Product\TierPrice $tierPrice */ foreach ($tierPriceCollection as $tierPrice) { - $this->assertEquals(0, $tierPrice->getExtensionAttributes()->getPercentageValue()); + $this->checkPercentageDiscount($tierPrice, $sku, $index); $this->assertEquals(0, $tierPrice->getExtensionAttributes()->getWebsiteId()); $tierPriceData = $tierPrice->getData(); unset($tierPriceData['extension_attributes']); $this->assertContains($tierPriceData, $this->expectedTierPrice[$sku]); + $index ++; } } } + /** + * Check percentage discount type. + * + * @param \Magento\Catalog\Model\Product\TierPrice $tierPrice + * @param string $sku + * @param int $index + * @return void + */ + private function checkPercentageDiscount( + \Magento\Catalog\Model\Product\TierPrice $tierPrice, + $sku, + $index + ) { + $this->assertEquals( + $this->expectedTierPrice[$sku][$index]['percentage_value'], + $tierPrice->getExtensionAttributes()->getPercentageValue() + ); + $tierPrice->setData('percentage_value', $tierPrice->getExtensionAttributes()->getPercentageValue()); + } + /** * @magentoAppArea adminhtml * @magentoDbIsolation enabled @@ -241,14 +281,16 @@ class AdvancedPricingTest extends \PHPUnit_Framework_TestCase foreach ($productIdList as $sku => $productId) { $product->load($productId); $tierPriceCollection = $product->getTierPrices(); - $this->assertEquals(3, count($tierPriceCollection)); + $this->assertEquals(4, count($tierPriceCollection)); + $index = 0; /** @var \Magento\Catalog\Model\Product\TierPrice $tierPrice */ foreach ($tierPriceCollection as $tierPrice) { - $this->assertEquals(0, $tierPrice->getExtensionAttributes()->getPercentageValue()); + $this->checkPercentageDiscount($tierPrice, $sku, $index); $this->assertEquals(0, $tierPrice->getExtensionAttributes()->getWebsiteId()); $tierPriceData = $tierPrice->getData(); unset($tierPriceData['extension_attributes']); $this->assertContains($tierPriceData, $this->expectedTierPrice[$sku]); + $index ++; } } } diff --git a/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Import/_files/import_advanced_pricing.csv b/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Import/_files/import_advanced_pricing.csv index 37c0dff622f56e9bcbba0bfab9136b5aab0de4cd..0c25cb37bf220f3f453d67065bd9b46fcc7a768b 100644 --- a/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Import/_files/import_advanced_pricing.csv +++ b/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Import/_files/import_advanced_pricing.csv @@ -1,7 +1,9 @@ -sku,tier_price_website,tier_price_customer_group,tier_price_qty,tier_price -"AdvancedPricingSimple 1","All Websites [USD]","ALL GROUPS",10.0000,300.0000 -"AdvancedPricingSimple 2","All Websites [USD]","ALL GROUPS",100.0000,1000000.0000 -"AdvancedPricingSimple 1","All Websites [USD]",General,11.0000,11.0000 -"AdvancedPricingSimple 2","All Websites [USD]","NOT LOGGED IN",12.0000,12.0000 -"AdvancedPricingSimple 1","All Websites [USD]",Retailer,14.0000,14.0000 -"AdvancedPricingSimple 2","All Websites [USD]",Wholesale,13.0000,13.0000 +sku,tier_price_website,tier_price_customer_group,tier_price_qty,tier_price,tier_price_value_type +"AdvancedPricingSimple 1","All Websites [USD]","ALL GROUPS",10.0000,300.0000,Fixed +"AdvancedPricingSimple 2","All Websites [USD]","ALL GROUPS",100.0000,1000000.0000,Fixed +"AdvancedPricingSimple 1","All Websites [USD]",General,11.0000,11.0000,Fixed +"AdvancedPricingSimple 2","All Websites [USD]","NOT LOGGED IN",12.0000,12.0000,Fixed +"AdvancedPricingSimple 1","All Websites [USD]",Retailer,14.0000,14.0000,Fixed +"AdvancedPricingSimple 2","All Websites [USD]",Wholesale,13.0000,13.0000,Fixed +"AdvancedPricingSimple 1","All Websites [USD]","ALL GROUPS",20.0000,50,Discount +"AdvancedPricingSimple 2","All Websites [USD]","ALL GROUPS",200.0000,50,Discount diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Catalog/adminhtml/js/utils/percentage-price-calculator.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Catalog/adminhtml/js/utils/percentage-price-calculator.test.js new file mode 100644 index 0000000000000000000000000000000000000000..8785e41c2920c9e5f132ab3fda3270611f10838b --- /dev/null +++ b/dev/tests/js/jasmine/tests/app/code/Magento/Catalog/adminhtml/js/utils/percentage-price-calculator.test.js @@ -0,0 +1,67 @@ +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +define(['Magento_Catalog/js/utils/percentage-price-calculator'], function (percentagePriceCalculator) { + 'use strict'; + + var basePrice = 100, + negativeBasePrice = -10, + decimalBasePrice = '100,1', + zeroBasePrice = 0; + + describe('Check valid calculation', function () { + it('5%', function () { + expect(percentagePriceCalculator(basePrice, '5%')).toBe('95.00'); + }); + it('0%', function () { + expect(percentagePriceCalculator(basePrice, '0%')).toBe('100.00'); + }); + it('100%', function () { + expect(percentagePriceCalculator(basePrice, '100%')).toBe('0.00'); + }); + it('110%', function () { + expect(percentagePriceCalculator(basePrice, '110%')).toBe('0.00'); + }); + it('5.5%', function () { + expect(percentagePriceCalculator(basePrice, '5.5%')).toBe('94.50'); + }); + it('.5%', function () { + expect(percentagePriceCalculator(basePrice, '.5%')).toBe('99.50'); + }); + it('-7%', function () { + expect(percentagePriceCalculator(basePrice, '-7%')).toBe('107.00'); + }); + }); + + describe('Check invalid input calculation', function () { + it('invalid with %', function () { + expect(percentagePriceCalculator(basePrice, '7p%')).toBe(''); + expect(percentagePriceCalculator(basePrice, '-%')).toBe(''); + }); + it('without %', function () { + expect(percentagePriceCalculator(basePrice, '7p')).toBe('7p'); + expect(percentagePriceCalculator(basePrice, '0')).toBe('0'); + expect(percentagePriceCalculator(basePrice, 'qwe')).toBe('qwe'); + }); + it('just %', function () { + expect(percentagePriceCalculator(basePrice, '%')).toBe(''); + }); + it('empty', function () { + expect(percentagePriceCalculator(basePrice, '')).toBe(''); + }); + }); + + describe('Other', function () { + it('negative base price', function () { + expect(percentagePriceCalculator(negativeBasePrice, '10%')).toBe('-9.00'); + }); + it('decimal base price', function () { + expect(percentagePriceCalculator(decimalBasePrice, '10%')).toBe('90.09'); + }); + it('zero base price', function () { + expect(percentagePriceCalculator(zeroBasePrice, '10%')).toBe('0.00'); + }); + }); +});