diff --git a/app/code/Magento/Bundle/Model/Product/Type.php b/app/code/Magento/Bundle/Model/Product/Type.php
index 4cfdf27fd0e6ab560cf296be35bdb8c5ea8e651e..3e8ae11c4faf362831ead26e549621ea483b9c7c 100644
--- a/app/code/Magento/Bundle/Model/Product/Type.php
+++ b/app/code/Magento/Bundle/Model/Product/Type.php
@@ -546,6 +546,7 @@ class Type extends \Magento\Catalog\Model\Product\Type\AbstractType
             $selectionsCollection = $this->_bundleCollection->create();
             $selectionsCollection->addAttributeToSelect('status');
             $selectionsCollection->addQuantityFilter();
+            $selectionsCollection->setFlag('product_children', true);
             $selectionsCollection->addFilterByRequiredOptions();
             $selectionsCollection->setOptionIdsFilter([$option->getId()]);
 
diff --git a/app/code/Magento/Bundle/Test/Unit/Model/Product/PriceTest.php b/app/code/Magento/Bundle/Test/Unit/Model/Product/PriceTest.php
index 9f7952e7ae8c882a5b747f9fd24855feea7c380e..10ca7335668470db793498273a998b64e34f4c4f 100644
--- a/app/code/Magento/Bundle/Test/Unit/Model/Product/PriceTest.php
+++ b/app/code/Magento/Bundle/Test/Unit/Model/Product/PriceTest.php
@@ -8,60 +8,67 @@ namespace Magento\Bundle\Test\Unit\Model\Product;
 use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
 
 /**
+ * Test for Model ProductPrice.
+ *
  * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  */
 class PriceTest extends \PHPUnit_Framework_TestCase
 {
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\CatalogRule\Model\ResourceModel\RuleFactory|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $ruleFactoryMock;
+    private $ruleFactoryMock;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $localeDateMock;
+    private $localeDateMock;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $storeManagerMock;
+    private $storeManagerMock;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\Customer\Model\Session|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $customerSessionMock;
+    private $customerSessionMock;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $eventManagerMock;
+    private $eventManagerMock;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\Catalog\Helper\Data|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $catalogHelperMock;
+    private $catalogHelperMock;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\Store\Model\Store|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $storeMock;
+    private $storeMock;
 
     /**
      * @var \Magento\Bundle\Model\Product\Price
      */
-    protected $model;
+    private $model;
 
     /**
      * @var \Magento\Framework\Pricing\PriceCurrencyInterface|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $priceCurrency;
+    private $priceCurrency;
 
     /**
-     * @var \Magento\Customer\Api\GroupManagementInterface
+     * @var \Magento\Customer\Api\GroupManagementInterface|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected $groupManagement;
+    private $groupManagement;
 
+    /**
+     * Set up.
+     *
+     * @return void
+     */
     protected function setUp()
     {
         $this->ruleFactoryMock = $this->getMock(
@@ -90,6 +97,7 @@ class PriceTest extends \PHPUnit_Framework_TestCase
             false
         );
         $scopeConfig = $this->getMock(\Magento\Framework\App\Config\ScopeConfigInterface::class);
+
         $objectManagerHelper = new ObjectManagerHelper($this);
         $this->model = $objectManagerHelper->getObject(
             \Magento\Bundle\Model\Product\Price::class,
@@ -109,6 +117,8 @@ class PriceTest extends \PHPUnit_Framework_TestCase
     }
 
     /**
+     * Test for calculateSpecialPrice().
+     *
      * @param float $finalPrice
      * @param float $specialPrice
      * @param int $callsNumber
@@ -118,6 +128,7 @@ class PriceTest extends \PHPUnit_Framework_TestCase
      * @covers \Magento\Bundle\Model\Product\Price::calculateSpecialPrice
      * @covers \Magento\Bundle\Model\Product\Price::__construct
      * @dataProvider calculateSpecialPrice
+     * @return void
      */
     public function testCalculateSpecialPrice($finalPrice, $specialPrice, $callsNumber, $dateInInterval, $expected)
     {
@@ -137,6 +148,8 @@ class PriceTest extends \PHPUnit_Framework_TestCase
     }
 
     /**
+     * Data provider for calculateSpecialPrice() test.
+     *
      * @return array
      */
     public function calculateSpecialPrice()
@@ -151,6 +164,11 @@ class PriceTest extends \PHPUnit_Framework_TestCase
         ];
     }
 
+    /**
+     * Test for getTotalBundleItemsPrice() with noCustom options.
+     *
+     * @return void
+     */
     public function testGetTotalBundleItemsPriceWithNoCustomOptions()
     {
         $productMock = $this->getMockBuilder(\Magento\Catalog\Model\Product::class)
@@ -165,8 +183,11 @@ class PriceTest extends \PHPUnit_Framework_TestCase
     }
 
     /**
+     * Test for getTotalBundleItemsPrice() with empty options.
+     *
      * @param string|null $value
      * @dataProvider dataProviderWithEmptyOptions
+     * @return void
      */
     public function testGetTotalBundleItemsPriceWithEmptyOptions($value)
     {
@@ -194,6 +215,8 @@ class PriceTest extends \PHPUnit_Framework_TestCase
     }
 
     /**
+     * Data provider for getTotalBundleItemsPrice() with empty options.
+     *
      * @return array
      */
     public function dataProviderWithEmptyOptions()
@@ -205,6 +228,11 @@ class PriceTest extends \PHPUnit_Framework_TestCase
         ];
     }
 
+    /**
+     * Test for getTotalBundleItemsPrice() with empty options.
+     *
+     * @return void
+     */
     public function testGetTotalBundleItemsPriceWithNoItems()
     {
         $storeId = 1;
@@ -240,9 +268,8 @@ class PriceTest extends \PHPUnit_Framework_TestCase
             ->method('getStoreId')
             ->willReturn($storeId);
 
-        $dataObjectMock->expects($this->once())
-            ->method('getValue')
-            ->willReturn('a:1:{i:0;s:1:"1";}');
+        $customOptionValue = 'a:1:{i:0;s:1:"1";}';
+        $dataObjectMock->expects($this->once())->method('getValue')->willReturn($customOptionValue);
         $productTypeMock->expects($this->once())
             ->method('getSelectionsByIds')
             ->with([1], $productMock)
diff --git a/app/code/Magento/Catalog/Api/BasePriceStorageInterface.php b/app/code/Magento/Catalog/Api/BasePriceStorageInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..013ec1f940047f1e2c9ce74f95c6f4eb679e04b5
--- /dev/null
+++ b/app/code/Magento/Catalog/Api/BasePriceStorageInterface.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Api;
+
+/**
+ * Base prices storage.
+ * @api
+ */
+interface BasePriceStorageInterface
+{
+    /**
+     * Return product prices.
+     *
+     * @param string[] $skus
+     * @return \Magento\Catalog\Api\Data\BasePriceInterface[]
+     */
+    public function get(array $skus);
+
+    /**
+     * Add or update product prices.
+     *
+     * @param \Magento\Catalog\Api\Data\BasePriceInterface[] $prices
+     * @return bool Will returned True if updated.
+     */
+    public function update(array $prices);
+}
diff --git a/app/code/Magento/Catalog/Api/CostStorageInterface.php b/app/code/Magento/Catalog/Api/CostStorageInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..0c9fb4d540d5bcbe58b92ffb3d1e3794556d39a6
--- /dev/null
+++ b/app/code/Magento/Catalog/Api/CostStorageInterface.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Api;
+
+/**
+ * Product cost storage.
+ * @api
+ */
+interface CostStorageInterface
+{
+    /**
+     * Return product prices.
+     *
+     * @param string[] $skus
+     * @return \Magento\Catalog\Api\Data\CostInterface[]
+     */
+    public function get(array $skus);
+
+    /**
+     * Add or update product cost.
+     *
+     * @param \Magento\Catalog\Api\Data\CostInterface[] $prices
+     * @return bool Will returned True if updated.
+     */
+    public function update(array $prices);
+
+    /**
+     * Delete product cost.
+     *
+     * @param string[] $skus
+     * @return bool Will returned True if deleted.
+     * @throws \Magento\Framework\Exception\CouldNotDeleteException
+     */
+    public function delete(array $skus);
+}
diff --git a/app/code/Magento/Catalog/Api/Data/BasePriceInterface.php b/app/code/Magento/Catalog/Api/Data/BasePriceInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..942de4a63abef74484e7169adbac07c9faa79cdf
--- /dev/null
+++ b/app/code/Magento/Catalog/Api/Data/BasePriceInterface.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Api\Data;
+
+/**
+ * Price interface.
+ * @api
+ */
+interface BasePriceInterface extends \Magento\Framework\Api\ExtensibleDataInterface
+{
+    /**#@+
+     * Constants
+     */
+    const PRICE = 'price';
+    const STORE_ID = 'store_id';
+    const SKU = 'sku';
+    /**#@-*/
+
+    /**
+     * Set price.
+     *
+     * @param float $price
+     * @return $this
+     */
+    public function setPrice($price);
+
+    /**
+     * Get price.
+     *
+     * @return float
+     */
+    public function getPrice();
+
+    /**
+     * Set store id.
+     *
+     * @param int $storeId
+     * @return $this
+     */
+    public function setStoreId($storeId);
+
+    /**
+     * Get store id.
+     *
+     * @return int
+     */
+    public function getStoreId();
+
+    /**
+     * Set SKU.
+     *
+     * @param string $sku
+     * @return $this
+     */
+    public function setSku($sku);
+
+    /**
+     * Get SKU.
+     *
+     * @return string
+     */
+    public function getSku();
+
+    /**
+     * Retrieve existing extension attributes object or create a new one.
+     *
+     * @return \Magento\Catalog\Api\Data\BasePriceExtensionInterface|null
+     */
+    public function getExtensionAttributes();
+
+    /**
+     * Set an extension attributes object.
+     *
+     * @param \Magento\Catalog\Api\Data\BasePriceExtensionInterface $extensionAttributes
+     * @return $this
+     */
+    public function setExtensionAttributes(
+        \Magento\Catalog\Api\Data\BasePriceExtensionInterface $extensionAttributes
+    );
+}
diff --git a/app/code/Magento/Catalog/Api/Data/CostInterface.php b/app/code/Magento/Catalog/Api/Data/CostInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..c007c81f1d7bf9181215044701b18205322be49b
--- /dev/null
+++ b/app/code/Magento/Catalog/Api/Data/CostInterface.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Api\Data;
+
+/**
+ * Cost interface.
+ * @api
+ */
+interface CostInterface extends \Magento\Framework\Api\ExtensibleDataInterface
+{
+    /**#@+
+     * Constants
+     */
+    const COST = 'cost';
+    const STORE_ID = 'store_id';
+    const SKU = 'sku';
+    /**#@-*/
+
+    /**
+     * Set cost value.
+     *
+     * @param float $cost
+     * @return $this
+     */
+    public function setCost($cost);
+
+    /**
+     * Get cost value.
+     *
+     * @return float
+     */
+    public function getCost();
+
+    /**
+     * Set store id.
+     *
+     * @param int $storeId
+     * @return $this
+     */
+    public function setStoreId($storeId);
+
+    /**
+     * Get store id.
+     *
+     * @return int
+     */
+    public function getStoreId();
+
+    /**
+     * Set SKU.
+     *
+     * @param string $sku
+     * @return $this
+     */
+    public function setSku($sku);
+
+    /**
+     * Get SKU.
+     *
+     * @return string
+     */
+    public function getSku();
+
+    /**
+     * Retrieve existing extension attributes object or create a new one.
+     *
+     * @return \Magento\Catalog\Api\Data\CostExtensionInterface|null
+     */
+    public function getExtensionAttributes();
+
+    /**
+     * Set an extension attributes object.
+     *
+     * @param \Magento\Catalog\Api\Data\CostExtensionInterface $extensionAttributes
+     * @return $this
+     */
+    public function setExtensionAttributes(
+        \Magento\Catalog\Api\Data\CostExtensionInterface $extensionAttributes
+    );
+}
diff --git a/app/code/Magento/Catalog/Api/Data/TierPriceInterface.php b/app/code/Magento/Catalog/Api/Data/TierPriceInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..1b708132c0d0ef95f900690e53b6520dac2fa008
--- /dev/null
+++ b/app/code/Magento/Catalog/Api/Data/TierPriceInterface.php
@@ -0,0 +1,134 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Api\Data;
+
+/**
+ * Tier price interface.
+ * @api
+ */
+interface TierPriceInterface extends \Magento\Framework\Api\ExtensibleDataInterface
+{
+    /**#@+
+     * Constants
+     */
+    const PRICE = 'price';
+    const PRICE_TYPE = 'price_type';
+    const WEBSITE_ID = 'website_id';
+    const SKU = 'sku';
+    const CUSTOMER_GROUP = 'customer_group';
+    const QUANTITY = 'quantity';
+    const PRICE_TYPE_FIXED = 'fixed';
+    const PRICE_TYPE_DISCOUNT = 'discount';
+    /**#@-*/
+
+    /**
+     * Set tier price.
+     *
+     * @param float $price
+     * @return $this
+     */
+    public function setPrice($price);
+
+    /**
+     * Get tier price.
+     *
+     * @return float
+     */
+    public function getPrice();
+
+    /**
+     * Set tier price type.
+     *
+     * @param string $type
+     * @return $this
+     */
+    public function setPriceType($type);
+
+    /**
+     * Get tier price type.
+     *
+     * @return string
+     */
+    public function getPriceType();
+
+    /**
+     * Set website id.
+     *
+     * @param int $websiteId
+     * @return $this
+     */
+    public function setWebsiteId($websiteId);
+
+    /**
+     * Get website id.
+     *
+     * @return int
+     */
+    public function getWebsiteId();
+
+    /**
+     * Set SKU.
+     *
+     * @param string $sku
+     * @return $this
+     */
+    public function setSku($sku);
+
+    /**
+     * Get SKU.
+     *
+     * @return string
+     */
+    public function getSku();
+
+    /**
+     * Set customer group.
+     *
+     * @param string $group
+     * @return $this
+     */
+    public function setCustomerGroup($group);
+
+    /**
+     * Get customer group.
+     *
+     * @return string
+     */
+    public function getCustomerGroup();
+
+    /**
+     * Set quantity.
+     *
+     * @param float $quantity
+     * @return $this
+     */
+    public function setQuantity($quantity);
+
+    /**
+     * Get quantity.
+     *
+     * @return float
+     */
+    public function getQuantity();
+
+    /**
+     * Retrieve existing extension attributes object or create a new one.
+     *
+     * @return \Magento\Catalog\Api\Data\TierPriceExtensionInterface|null
+     */
+    public function getExtensionAttributes();
+
+    /**
+     * Set an extension attributes object.
+     *
+     * @param \Magento\Catalog\Api\Data\TierPriceExtensionInterface $extensionAttributes
+     * @return $this
+     */
+    public function setExtensionAttributes(
+        \Magento\Catalog\Api\Data\TierPriceExtensionInterface $extensionAttributes
+    );
+}
diff --git a/app/code/Magento/Catalog/Api/TierPriceStorageInterface.php b/app/code/Magento/Catalog/Api/TierPriceStorageInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..200cdc1baa411c0ec63b9161f5829c1b29724315
--- /dev/null
+++ b/app/code/Magento/Catalog/Api/TierPriceStorageInterface.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Api;
+
+/**
+ * Tier prices storage.
+ * @api
+ */
+interface TierPriceStorageInterface
+{
+    /**
+     * Return product prices.
+     *
+     * @param string[] $skus
+     * @return \Magento\Catalog\Api\Data\TierPriceInterface[]
+     */
+    public function get(array $skus);
+
+    /**
+     * Add or update product prices.
+     *
+     * @param \Magento\Catalog\Api\Data\TierPriceInterface[] $prices
+     * @return bool Will returned True if updated.
+     */
+    public function update(array $prices);
+
+    /**
+     * Remove existing tier prices and replace them with the new ones.
+     *
+     * @param \Magento\Catalog\Api\Data\TierPriceInterface[] $prices
+     * @return bool Will returned True if replaced.
+     */
+    public function replace(array $prices);
+
+    /**
+     * Delete product tier prices.
+     *
+     * @param \Magento\Catalog\Api\Data\TierPriceInterface[] $prices
+     * @return bool Will returned True if deleted.
+     */
+    public function delete(array $prices);
+}
diff --git a/app/code/Magento/Catalog/Model/Config/Source/Product/Options/Price.php b/app/code/Magento/Catalog/Model/Config/Source/Product/Options/Price.php
index 5e518df37db1a574f25fe008510e75742cdffa9c..b994c787bee7aa76f8a4baa3648bcbb2c0e4ee27 100644
--- a/app/code/Magento/Catalog/Model/Config/Source/Product/Options/Price.php
+++ b/app/code/Magento/Catalog/Model/Config/Source/Product/Options/Price.php
@@ -23,7 +23,7 @@ class Price implements ProductPriceOptionsInterface
     {
         return [
             ['value' => self::VALUE_FIXED, 'label' => __('Fixed')],
-            ['value' => self::VALUE_PERCENT, 'label' => __('Discount')],
+            ['value' => self::VALUE_PERCENT, 'label' => __('Percent')],
         ];
     }
 }
diff --git a/app/code/Magento/Catalog/Model/Config/Source/Product/Options/TierPrice.php b/app/code/Magento/Catalog/Model/Config/Source/Product/Options/TierPrice.php
new file mode 100644
index 0000000000000000000000000000000000000000..d630f4890fc95afd894ab88b81aedc552e9bc50c
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/Config/Source/Product/Options/TierPrice.php
@@ -0,0 +1,27 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Catalog\Model\Config\Source\Product\Options;
+
+use Magento\Catalog\Model\Config\Source\ProductPriceOptionsInterface;
+
+/**
+ * TierPrice types mode source.
+ */
+class TierPrice implements ProductPriceOptionsInterface
+{
+    /**
+     * {@inheritdoc}
+     *
+     * @codeCoverageIgnore
+     */
+    public function toOptionArray()
+    {
+        return [
+            ['value' => self::VALUE_FIXED, 'label' => __('Fixed')],
+            ['value' => self::VALUE_PERCENT, 'label' => __('Discount')],
+        ];
+    }
+}
diff --git a/app/code/Magento/Catalog/Model/Product/Price/BasePrice.php b/app/code/Magento/Catalog/Model/Product/Price/BasePrice.php
new file mode 100644
index 0000000000000000000000000000000000000000..b7c01141de33bb7202ccad4eff2605bb7bdcbae8
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/Product/Price/BasePrice.php
@@ -0,0 +1,79 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Model\Product\Price;
+
+use Magento\Catalog\Api\Data\BasePriceInterface;
+
+/**
+ * Product Base Price DTO.
+ */
+class BasePrice extends \Magento\Framework\Model\AbstractExtensibleModel implements BasePriceInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function setPrice($price)
+    {
+        return $this->setData(self::PRICE, $price);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getPrice()
+    {
+        return $this->getData(self::PRICE);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setStoreId($storeId)
+    {
+        return $this->setData(self::STORE_ID, $storeId);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getStoreId()
+    {
+        return $this->getData(self::STORE_ID);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setSku($sku)
+    {
+        return $this->setData(self::SKU, $sku);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSku()
+    {
+        return $this->getData(self::SKU);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getExtensionAttributes()
+    {
+        return $this->_getExtensionAttributes();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setExtensionAttributes(\Magento\Catalog\Api\Data\BasePriceExtensionInterface $extensionAttributes)
+    {
+        return $this->_setExtensionAttributes($extensionAttributes);
+    }
+}
diff --git a/app/code/Magento/Catalog/Model/Product/Price/BasePriceStorage.php b/app/code/Magento/Catalog/Model/Product/Price/BasePriceStorage.php
new file mode 100644
index 0000000000000000000000000000000000000000..e69f89f0bb146d62e7cc65d1022a4d25b6738c4f
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/Product/Price/BasePriceStorage.php
@@ -0,0 +1,227 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Model\Product\Price;
+
+/**
+ * Base prices storage.
+ */
+class BasePriceStorage implements \Magento\Catalog\Api\BasePriceStorageInterface
+{
+    /**
+     * Attribute code.
+     *
+     * @var string
+     */
+    private $attributeCode = 'price';
+
+    /**
+     * @var PricePersistence
+     */
+    private $pricePersistence;
+
+    /**
+     * @var \Magento\Catalog\Api\Data\BasePriceInterfaceFactory
+     */
+    private $basePriceInterfaceFactory;
+
+    /**
+     * @var \Magento\Catalog\Model\ProductIdLocatorInterface
+     */
+    private $productIdLocator;
+
+    /**
+     * @var \Magento\Store\Api\StoreRepositoryInterface
+     */
+    private $storeRepository;
+
+    /**
+     * @var \Magento\Catalog\Api\ProductRepositoryInterface
+     */
+    private $productRepository;
+
+    /**
+     * Price type allowed.
+     *
+     * @var int
+     */
+    private $priceTypeAllowed = 1;
+
+    /**
+     * Allowed product types.
+     *
+     * @var array
+     */
+    private $allowedProductTypes = [];
+
+    /**
+     * @var PricePersistenceFactory
+     */
+    private $pricePersistenceFactory;
+
+    /**
+     * BasePriceStorage constructor.
+     *
+     * @param PricePersistenceFactory $pricePersistenceFactory
+     * @param \Magento\Catalog\Api\Data\BasePriceInterfaceFactory $basePriceInterfaceFactory
+     * @param \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator
+     * @param \Magento\Store\Api\StoreRepositoryInterface $storeRepository
+     * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository
+     * @param array $allowedProductTypes
+     */
+    public function __construct(
+        PricePersistenceFactory $pricePersistenceFactory,
+        \Magento\Catalog\Api\Data\BasePriceInterfaceFactory $basePriceInterfaceFactory,
+        \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator,
+        \Magento\Store\Api\StoreRepositoryInterface $storeRepository,
+        \Magento\Catalog\Api\ProductRepositoryInterface $productRepository,
+        array $allowedProductTypes = []
+    ) {
+        $this->pricePersistenceFactory = $pricePersistenceFactory;
+        $this->basePriceInterfaceFactory = $basePriceInterfaceFactory;
+        $this->productIdLocator = $productIdLocator;
+        $this->storeRepository = $storeRepository;
+        $this->productRepository = $productRepository;
+        $this->allowedProductTypes = $allowedProductTypes;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function get(array $skus)
+    {
+        $this->validateSkus($skus);
+        $rawPrices = $this->getPricePersistence()->get($skus);
+        $prices = [];
+        foreach ($rawPrices as $rawPrice) {
+            $price = $this->basePriceInterfaceFactory->create();
+            $sku = $this->getPricePersistence()
+                ->retrieveSkuById($rawPrice[$this->getPricePersistence()->getEntityLinkField()], $skus);
+            $price->setSku($sku);
+            $price->setPrice($rawPrice['value']);
+            $price->setStoreId($rawPrice['store_id']);
+            $prices[] = $price;
+        }
+
+        return $prices;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function update(array $prices)
+    {
+        $this->validate($prices);
+        $formattedPrices = [];
+
+        foreach ($prices as $price) {
+            $ids = array_keys($this->productIdLocator->retrieveProductIdsBySkus([$price->getSku()])[$price->getSku()]);
+            foreach ($ids as $id) {
+                $formattedPrices[] = [
+                    'store_id' => $price->getStoreId(),
+                    $this->getPricePersistence()->getEntityLinkField() => $id,
+                    'value' => $price->getPrice(),
+                ];
+            }
+        }
+
+        $this->getPricePersistence()->update($formattedPrices);
+
+        return true;
+    }
+
+    /**
+     * Get price persistence.
+     *
+     * @return PricePersistence
+     */
+    private function getPricePersistence()
+    {
+        if (!$this->pricePersistence) {
+            $this->pricePersistence = $this->pricePersistenceFactory->create(['attributeCode' => $this->attributeCode]);
+        }
+
+        return $this->pricePersistence;
+    }
+
+    /**
+     * Validate SKU, check product types and skip not existing products.
+     *
+     * @param array $skus
+     * @throws \Magento\Framework\Exception\LocalizedException
+     * @return void
+     */
+    private function validateSkus(array $skus)
+    {
+        $idsBySku = $this->productIdLocator->retrieveProductIdsBySkus($skus);
+        $skuDiff = array_diff($skus, array_keys($idsBySku));
+
+        foreach ($idsBySku as $sku => $ids) {
+            foreach ($ids as $type) {
+                if (!in_array($type, $this->allowedProductTypes)
+                    || (
+                        $type == \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE
+                        && $this->productRepository->get($sku)->getPriceType() != $this->priceTypeAllowed
+                    )
+                ) {
+                    $skuDiff[] = $sku;
+                    break;
+                }
+            }
+        }
+
+        if (!empty($skuDiff)) {
+            $values = implode(', ', $skuDiff);
+            $description = count($skuDiff) == 1
+                ? __('Requested product doesn\'t exist: %1', $values)
+                : __('Requested products don\'t exist: %1', $values);
+            throw new \Magento\Framework\Exception\NoSuchEntityException($description);
+        }
+    }
+
+    /**
+     * Validate that prices have appropriate values.
+     *
+     * @param array $prices
+     * @throws \Magento\Framework\Exception\LocalizedException
+     * @return void
+     */
+    private function validate(array $prices)
+    {
+        $skus = array_unique(
+            array_map(function ($price) {
+                if (!$price->getSku()) {
+                    throw new \Magento\Framework\Exception\LocalizedException(
+                        __(
+                            'Invalid attribute %fieldName: %fieldValue.',
+                            [
+                                'fieldName' => 'sku',
+                                'fieldValue' => $price->getSku()
+                            ]
+                        )
+                    );
+                }
+                return $price->getSku();
+            }, $prices)
+        );
+        $this->validateSkus($skus);
+
+        foreach ($prices as $price) {
+            if (null === $price->getPrice() || $price->getPrice() < 0) {
+                throw new \Magento\Framework\Exception\LocalizedException(
+                    __(
+                        'Invalid attribute %fieldName: %fieldValue.',
+                        [
+                            'fieldName' => 'Price',
+                            'fieldValue' => $price->getPrice()
+                        ]
+                    )
+                );
+            }
+            $this->storeRepository->getById($price->getStoreId());
+        }
+    }
+}
diff --git a/app/code/Magento/Catalog/Model/Product/Price/Cost.php b/app/code/Magento/Catalog/Model/Product/Price/Cost.php
new file mode 100644
index 0000000000000000000000000000000000000000..8d52c578ea94b431254883d0e815c1f0064c22d2
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/Product/Price/Cost.php
@@ -0,0 +1,79 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Model\Product\Price;
+
+use Magento\Catalog\Api\Data\CostInterface;
+
+/**
+ * Product Cost DTO.
+ */
+class Cost extends \Magento\Framework\Model\AbstractExtensibleModel implements CostInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function setCost($cost)
+    {
+        return $this->setData(self::COST, $cost);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getCost()
+    {
+        return $this->getData(self::COST);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setStoreId($storeId)
+    {
+        return $this->setData(self::STORE_ID, $storeId);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getStoreId()
+    {
+        return $this->getData(self::STORE_ID);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setSku($sku)
+    {
+        return $this->setData(self::SKU, $sku);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSku()
+    {
+        return $this->getData(self::SKU);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getExtensionAttributes()
+    {
+        return $this->_getExtensionAttributes();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setExtensionAttributes(\Magento\Catalog\Api\Data\CostExtensionInterface $extensionAttributes)
+    {
+        return $this->_setExtensionAttributes($extensionAttributes);
+    }
+}
diff --git a/app/code/Magento/Catalog/Model/Product/Price/CostStorage.php b/app/code/Magento/Catalog/Model/Product/Price/CostStorage.php
new file mode 100644
index 0000000000000000000000000000000000000000..e7fc682514a3fa3c575f13938a0a86048a0e50fb
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/Product/Price/CostStorage.php
@@ -0,0 +1,218 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Model\Product\Price;
+
+/**
+ * Product cost storage.
+ */
+class CostStorage implements \Magento\Catalog\Api\CostStorageInterface
+{
+    /**
+     * Attribute code.
+     *
+     * @var string
+     */
+    private $attributeCode = 'cost';
+
+    /**
+     * @var PricePersistence
+     */
+    private $pricePersistence;
+
+    /**
+     * @var \Magento\Catalog\Api\Data\CostInterfaceFactory
+     */
+    private $costInterfaceFactory;
+
+    /**
+     * @var \Magento\Catalog\Model\ProductIdLocatorInterface
+     */
+    private $productIdLocator;
+
+    /**
+     * Allowed product types.
+     *
+     * @var array
+     */
+    private $allowedProductTypes = [];
+
+    /**
+     * @var PricePersistenceFactory
+     */
+    private $pricePersistenceFactory;
+
+    /**
+     * @var \Magento\Store\Api\StoreRepositoryInterface
+     */
+    private $storeRepository;
+
+    /**
+     * CostStorage constructor.
+     *
+     * @param PricePersistenceFactory $pricePersistenceFactory
+     * @param \Magento\Catalog\Api\Data\CostInterfaceFactory $costInterfaceFactory
+     * @param \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator
+     * @param \Magento\Store\Api\StoreRepositoryInterface $storeRepository
+     * @param array $allowedProductTypes
+     */
+    public function __construct(
+        PricePersistenceFactory $pricePersistenceFactory,
+        \Magento\Catalog\Api\Data\CostInterfaceFactory $costInterfaceFactory,
+        \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator,
+        \Magento\Store\Api\StoreRepositoryInterface $storeRepository,
+        array $allowedProductTypes = []
+    ) {
+        $this->pricePersistenceFactory = $pricePersistenceFactory;
+        $this->costInterfaceFactory = $costInterfaceFactory;
+        $this->productIdLocator = $productIdLocator;
+        $this->storeRepository = $storeRepository;
+        $this->allowedProductTypes = $allowedProductTypes;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function get(array $skus)
+    {
+        $this->validateSkus($skus);
+        $rawPrices = $this->getPricePersistence()->get($skus);
+        $prices = [];
+        foreach ($rawPrices as $rawPrice) {
+            $price = $this->costInterfaceFactory->create();
+            $sku = $this->getPricePersistence()
+                ->retrieveSkuById($rawPrice[$this->getPricePersistence()->getEntityLinkField()], $skus);
+            $price->setSku($sku);
+            $price->setCost($rawPrice['value']);
+            $price->setStoreId($rawPrice['store_id']);
+            $prices[] = $price;
+        }
+
+        return $prices;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function update(array $prices)
+    {
+        $this->validate($prices);
+        $formattedPrices = [];
+
+        foreach ($prices as $price) {
+            $ids = array_keys($this->productIdLocator->retrieveProductIdsBySkus([$price->getSku()])[$price->getSku()]);
+            foreach ($ids as $id) {
+                $formattedPrices[] = [
+                    'store_id' => $price->getStoreId(),
+                    $this->getPricePersistence()->getEntityLinkField() => $id,
+                    'value' => $price->getCost(),
+                ];
+            }
+        }
+
+        $this->getPricePersistence()->update($formattedPrices);
+
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function delete(array $skus)
+    {
+        $this->validateSkus($skus);
+        $this->getPricePersistence()->delete($skus);
+
+        return true;
+    }
+
+    /**
+     * Get price persistence.
+     *
+     * @return PricePersistence
+     */
+    private function getPricePersistence()
+    {
+        if (!$this->pricePersistence) {
+            $this->pricePersistence = $this->pricePersistenceFactory->create(['attributeCode' => $this->attributeCode]);
+        }
+
+        return $this->pricePersistence;
+    }
+
+    /**
+     * Validate that prices have appropriate values.
+     *
+     * @param array $prices
+     * @throws \Magento\Framework\Exception\LocalizedException
+     * @return void
+     */
+    private function validate(array $prices)
+    {
+        $skus = array_unique(
+            array_map(function ($price) {
+                if (!$price->getSku()) {
+                    throw new \Magento\Framework\Exception\LocalizedException(
+                        __(
+                            'Invalid attribute %fieldName: %fieldValue.',
+                            [
+                                'fieldName' => 'sku',
+                                'fieldValue' => $price->getSku()
+                            ]
+                        )
+                    );
+                }
+                return $price->getSku();
+            }, $prices)
+        );
+        $this->validateSkus($skus);
+
+        foreach ($prices as $price) {
+            if (null === $price->getCost() || $price->getCost() < 0) {
+                throw new \Magento\Framework\Exception\LocalizedException(
+                    __(
+                        'Invalid attribute %fieldName: %fieldValue.',
+                        [
+                            'fieldName' => 'Cost',
+                            'fieldValue' => $price->getCost()
+                        ]
+                    )
+                );
+            }
+            $this->storeRepository->getById($price->getStoreId());
+        }
+    }
+
+    /**
+     * Validate SKU, check product types and skip not existing products.
+     *
+     * @param array $skus
+     * @throws \Magento\Framework\Exception\LocalizedException
+     * @return void
+     */
+    private function validateSkus(array $skus)
+    {
+        $idsBySku = $this->productIdLocator->retrieveProductIdsBySkus($skus);
+        $skuDiff = array_diff($skus, array_keys($idsBySku));
+
+        foreach ($idsBySku as $sku => $ids) {
+            foreach (array_values($ids) as $type) {
+                if (!in_array($type, $this->allowedProductTypes)) {
+                    $skuDiff[] = $sku;
+                    break;
+                }
+            }
+        }
+
+        if (!empty($skuDiff)) {
+            $values = implode(', ', $skuDiff);
+            $description = count($skuDiff) == 1
+                ? __('Requested product doesn\'t exist: %1', $values)
+                : __('Requested products don\'t exist: %1', $values);
+            throw new \Magento\Framework\Exception\NoSuchEntityException($description);
+        }
+    }
+}
diff --git a/app/code/Magento/Catalog/Model/Product/Price/PricePersistence.php b/app/code/Magento/Catalog/Model/Product/Price/PricePersistence.php
new file mode 100644
index 0000000000000000000000000000000000000000..f37fb15cd47e433e4125b0bd51d90d00ae291cf2
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/Product/Price/PricePersistence.php
@@ -0,0 +1,228 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Model\Product\Price;
+
+/**
+ * Price persistence.
+ */
+class PricePersistence
+{
+    /**
+     * Price storage table.
+     *
+     * @var string
+     */
+    private $table = 'catalog_product_entity_decimal';
+
+    /**
+     * @var \Magento\Catalog\Model\ResourceModel\Attribute
+     */
+    private $attributeResource;
+
+    /**
+     * @var \Magento\Catalog\Api\ProductAttributeRepositoryInterface
+     */
+    private $attributeRepository;
+
+    /**
+     * @var \Magento\Catalog\Model\ProductIdLocatorInterface
+     */
+    private $productIdLocator;
+
+    /**
+     * Metadata pool.
+     *
+     * @var \Magento\Framework\EntityManager\MetadataPool
+     */
+    private $metadataPool;
+
+    /**
+     * Attribute code.
+     *
+     * @var string
+     */
+    private $attributeCode;
+
+    /**
+     * Attribute ID.
+     *
+     * @var int
+     */
+    private $attributeId;
+
+    /**
+     * Items per operation.
+     *
+     * @var int
+     */
+    private $itemsPerOperation = 500;
+
+    /**
+     * PricePersistence constructor.
+     *
+     * @param \Magento\Catalog\Model\ResourceModel\Attribute $attributeResource
+     * @param \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository
+     * @param \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator
+     * @param \Magento\Framework\EntityManager\MetadataPool $metadataPool
+     * @param string $attributeCode
+     */
+    public function __construct(
+        \Magento\Catalog\Model\ResourceModel\Attribute $attributeResource,
+        \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository,
+        \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator,
+        \Magento\Framework\EntityManager\MetadataPool $metadataPool,
+        $attributeCode = ''
+    ) {
+        $this->attributeResource = $attributeResource;
+        $this->attributeRepository = $attributeRepository;
+        $this->attributeCode = $attributeCode;
+        $this->productIdLocator = $productIdLocator;
+        $this->metadataPool = $metadataPool;
+    }
+
+    /**
+     * Get prices by SKUs.
+     *
+     * @param array $skus
+     * @return array
+     */
+    public function get(array $skus)
+    {
+        $ids = $this->retrieveAffectedIds($skus);
+        $select = $this->attributeResource->getConnection()
+            ->select()
+            ->from($this->attributeResource->getTable($this->table));
+        return $this->attributeResource->getConnection()->fetchAll(
+            $select->where($this->getEntityLinkField() . ' IN (?)', $ids)
+                ->where('attribute_id = ?', $this->getAttributeId())
+        );
+    }
+
+    /**
+     * Update prices.
+     *
+     * @param array $prices
+     * @return void
+     * @throws \Magento\Framework\Exception\CouldNotSaveException
+     */
+    public function update(array $prices)
+    {
+        array_walk($prices, function (&$price) {
+            return $price['attribute_id'] = $this->getAttributeId();
+        });
+        $connection = $this->attributeResource->getConnection();
+        $connection->beginTransaction();
+        try {
+            foreach (array_chunk($prices, $this->itemsPerOperation) as $pricesBunch) {
+                $this->attributeResource->getConnection()->insertOnDuplicate(
+                    $this->attributeResource->getTable($this->table),
+                    $pricesBunch,
+                    ['value']
+                );
+            }
+            $connection->commit();
+        } catch (\Exception $e) {
+            $connection->rollBack();
+            throw new \Magento\Framework\Exception\CouldNotSaveException(
+                __('Could not save Prices.'),
+                $e
+            );
+        }
+    }
+
+    /**
+     * Delete product attribute by SKU.
+     *
+     * @param array $skus
+     * @return void
+     * @throws \Magento\Framework\Exception\CouldNotDeleteException
+     */
+    public function delete(array $skus)
+    {
+        $ids = $this->retrieveAffectedIds($skus);
+        $connection = $this->attributeResource->getConnection();
+        $connection->beginTransaction();
+        try {
+            foreach (array_chunk($ids, $this->itemsPerOperation) as $idsBunch) {
+                $this->attributeResource->getConnection()->delete(
+                    $this->attributeResource->getTable($this->table),
+                    [
+                        'attribute_id = ?' => $this->getAttributeId(),
+                        $this->getEntityLinkField() . ' IN (?)' => $idsBunch
+                    ]
+                );
+            }
+            $connection->commit();
+        } catch (\Exception $e) {
+            $connection->rollBack();
+            throw new \Magento\Framework\Exception\CouldNotDeleteException(
+                __('Could not delete Prices'),
+                $e
+            );
+        }
+    }
+
+    /**
+     * Retrieve SKU by product ID.
+     *
+     * @param int $id
+     * @param array $skus
+     * @return int|null
+     */
+    public function retrieveSkuById($id, $skus)
+    {
+        foreach ($this->productIdLocator->retrieveProductIdsBySkus($skus) as $sku => $ids) {
+            if (false !== array_key_exists($id, $ids)) {
+                return $sku;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Get attribute ID.
+     *
+     * @return int
+     */
+    private function getAttributeId()
+    {
+        if (!$this->attributeId) {
+            $this->attributeId = $this->attributeRepository->get($this->attributeCode)->getAttributeId();
+        }
+
+        return $this->attributeId;
+    }
+
+    /**
+     * Retrieve affected product IDs.
+     *
+     * @param array $skus
+     * @return array
+     */
+    private function retrieveAffectedIds(array $skus)
+    {
+        $affectedIds = [];
+
+        foreach ($this->productIdLocator->retrieveProductIdsBySkus($skus) as $productIds) {
+            $affectedIds = array_merge($affectedIds, array_keys($productIds));
+        }
+
+        return array_unique($affectedIds);
+    }
+
+    /**
+     * Get link field.
+     *
+     * @return string
+     */
+    public function getEntityLinkField()
+    {
+        return $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class)
+            ->getLinkField();
+    }
+}
diff --git a/app/code/Magento/Catalog/Model/Product/Price/TierPrice.php b/app/code/Magento/Catalog/Model/Product/Price/TierPrice.php
new file mode 100644
index 0000000000000000000000000000000000000000..c3c30d18fe639df371a6a5b3d51f7f06492be4f5
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/Product/Price/TierPrice.php
@@ -0,0 +1,127 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Model\Product\Price;
+
+use Magento\Catalog\Api\Data\TierPriceInterface;
+
+/**
+ * TierPrice DTO.
+ */
+class TierPrice extends \Magento\Framework\Model\AbstractExtensibleModel implements TierPriceInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function setPrice($price)
+    {
+        return $this->setData(self::PRICE, $price);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getPrice()
+    {
+        return $this->getData(self::PRICE);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setPriceType($type)
+    {
+        return $this->setData(self::PRICE_TYPE, $type);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getPriceType()
+    {
+        return $this->getData(self::PRICE_TYPE);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setWebsiteId($websiteId)
+    {
+        return $this->setData(self::WEBSITE_ID, $websiteId);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getWebsiteId()
+    {
+        return $this->getData(self::WEBSITE_ID);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setSku($sku)
+    {
+        return $this->setData(self::SKU, $sku);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSku()
+    {
+        return $this->getData(self::SKU);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setCustomerGroup($group)
+    {
+        return $this->setData(self::CUSTOMER_GROUP, $group);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getCustomerGroup()
+    {
+        return $this->getData(self::CUSTOMER_GROUP);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setQuantity($quantity)
+    {
+        return $this->setData(self::QUANTITY, $quantity);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getQuantity()
+    {
+        return $this->getData(self::QUANTITY);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getExtensionAttributes()
+    {
+        return $this->_getExtensionAttributes();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setExtensionAttributes(\Magento\Catalog\Api\Data\TierPriceExtensionInterface $extensionAttributes)
+    {
+        return $this->_setExtensionAttributes($extensionAttributes);
+    }
+}
diff --git a/app/code/Magento/Catalog/Model/Product/Price/TierPriceFactory.php b/app/code/Magento/Catalog/Model/Product/Price/TierPriceFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..1e031649ebdcf2830826e57dccb974a51b7efa71
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/Product/Price/TierPriceFactory.php
@@ -0,0 +1,169 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Model\Product\Price;
+
+use Magento\Catalog\Api\Data\TierPriceInterface;
+use Magento\Framework\Exception\NoSuchEntityException;
+
+/**
+ * Tier price factory.
+ */
+class TierPriceFactory
+{
+    /**
+     * Tier price factory.
+     *
+     * @var \Magento\Catalog\Api\Data\TierPriceInterfaceFactory
+     */
+    private $tierPriceFactory;
+
+    /**
+     * Tier price persistence.
+     *
+     * @var TierPricePersistence
+     */
+    private $tierPricePersistence;
+
+    /**
+     * Customer group repository.
+     *
+     * @var \Magento\Customer\Api\GroupRepositoryInterface
+     */
+    private $customerGroupRepository;
+
+    /**
+     * All groups value.
+     *
+     * @var string
+     */
+    private $allGroupsValue = 'all groups';
+
+    /**
+     * All groups ID.
+     *
+     * @var int
+     */
+    private $allGroupsId = 1;
+
+    /**
+     * Customer groups by code.
+     *
+     * @var array
+     */
+    private $customerGroupsByCode = [];
+
+    /**
+     * TierPriceBuilder constructor.
+     *
+     * @param \Magento\Catalog\Api\Data\TierPriceInterfaceFactory $tierPriceFactory
+     * @param TierPricePersistence $tierPricePersistence
+     * @param \Magento\Customer\Api\GroupRepositoryInterface $customerGroupRepository
+     * @param \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder
+     * @param \Magento\Framework\Api\FilterBuilder $filterBuilder
+     */
+    public function __construct(
+        \Magento\Catalog\Api\Data\TierPriceInterfaceFactory $tierPriceFactory,
+        TierPricePersistence $tierPricePersistence,
+        \Magento\Customer\Api\GroupRepositoryInterface $customerGroupRepository,
+        \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder,
+        \Magento\Framework\Api\FilterBuilder $filterBuilder
+    ) {
+        $this->tierPriceFactory = $tierPriceFactory;
+        $this->tierPricePersistence = $tierPricePersistence;
+        $this->customerGroupRepository = $customerGroupRepository;
+        $this->searchCriteriaBuilder = $searchCriteriaBuilder;
+        $this->filterBuilder = $filterBuilder;
+    }
+
+    /**
+     * Create populated tier price DTO.
+     *
+     * @param array $rawPrice
+     * @param string $sku
+     * @return \Magento\Catalog\Api\Data\TierPriceInterface
+     */
+    public function create(array $rawPrice, $sku)
+    {
+        $price = $this->tierPriceFactory->create();
+        $price->setPrice(isset($rawPrice['percentage_value']) ? $rawPrice['percentage_value'] : $rawPrice['value']);
+        $price->setPriceType(
+            isset($rawPrice['percentage_value'])
+            ? TierPriceInterface::PRICE_TYPE_DISCOUNT
+            : TierPriceInterface::PRICE_TYPE_FIXED
+        );
+        $price->setWebsiteId($rawPrice['website_id']);
+        $price->setSku($sku);
+        $price->setCustomerGroup(
+            $rawPrice['all_groups'] == $this->allGroupsId
+            ? $this->allGroupsValue
+            : $this->customerGroupRepository->getById($rawPrice['customer_group_id'])->getCode()
+        );
+        $price->setQuantity($rawPrice['qty']);
+
+        return $price;
+    }
+
+    /**
+     * Build tier price skeleton that has DB consistent format.
+     *
+     * @param TierPriceInterface $price
+     * @param int $id
+     * @return array
+     */
+    public function createSkeleton(TierPriceInterface $price, $id)
+    {
+        return [
+            $this->tierPricePersistence->getEntityLinkField() => $id,
+            'all_groups' => $this->retrievePriceForAllGroupsValue($price),
+            'customer_group_id' => $this->retrievePriceForAllGroupsValue($price) === $this->allGroupsId
+                ? 0
+                : $this->retrieveGroupValue(strtolower($price->getCustomerGroup())),
+            'qty' => $price->getQuantity(),
+            'value' => $price->getPriceType() === TierPriceInterface::PRICE_TYPE_FIXED
+                ? $price->getPrice()
+                : 0.00,
+            'percentage_value' => $price->getPriceType() === TierPriceInterface::PRICE_TYPE_DISCOUNT
+                ? $price->getPrice()
+                : null,
+            'website_id' => $price->getWebsiteId()
+        ];
+    }
+
+    /**
+     * Retrieve price for all groups value.
+     *
+     * @param TierPriceInterface $price
+     * @return int
+     */
+    private function retrievePriceForAllGroupsValue(TierPriceInterface $price)
+    {
+        return strcasecmp($price->getCustomerGroup(), $this->allGroupsValue) === 0 ? $this->allGroupsId : 0;
+    }
+
+    /**
+     * Retrieve customer group id by code.
+     *
+     * @param string $code
+     * @return int
+     * @throws NoSuchEntityException
+     */
+    private function retrieveGroupValue($code)
+    {
+        if (!isset($this->customerGroupsByCode[$code])) {
+            $searchCriteria = $this->searchCriteriaBuilder->addFilters(
+                [
+                    $this->filterBuilder->setField('customer_group_code')->setValue($code)->create()
+                ]
+            );
+            $items = $this->customerGroupRepository->getList($searchCriteria->create())->getItems();
+            $item = array_shift($items);
+            $this->customerGroupsByCode[strtolower($item->getCode())] = $item->getId();
+        }
+
+        return $this->customerGroupsByCode[$code];
+    }
+}
diff --git a/app/code/Magento/Catalog/Model/Product/Price/TierPricePersistence.php b/app/code/Magento/Catalog/Model/Product/Price/TierPricePersistence.php
new file mode 100644
index 0000000000000000000000000000000000000000..01293d0532fbfc82ed0893436baad30e2baa87b8
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/Product/Price/TierPricePersistence.php
@@ -0,0 +1,166 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Model\Product\Price;
+
+/**
+ * Persists tier prices.
+ */
+class TierPricePersistence
+{
+    /**
+     * Number or items per each operation.
+     *
+     * @var int
+     */
+    private $itemsPerOperation = 500;
+
+    /**
+     * Tier price resource model.
+     *
+     * @var \Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Tierprice
+     */
+    private $tierpriceResource;
+
+    /**
+     * Metadata pool.
+     *
+     * @var \Magento\Framework\EntityManager\MetadataPool
+     */
+    private $metadataPool;
+
+    /**
+     * TierPricePersister constructor.
+     *
+     * @param \Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Tierprice $tierpriceResource
+     * @param \Magento\Framework\EntityManager\MetadataPool $metadataPool
+     */
+    public function __construct(
+        \Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Tierprice $tierpriceResource,
+        \Magento\Framework\EntityManager\MetadataPool $metadataPool
+    ) {
+        $this->tierpriceResource = $tierpriceResource;
+        $this->metadataPool = $metadataPool;
+    }
+
+    /**
+     * Get tier prices by product IDs.
+     *
+     * @param array $ids
+     * @return array
+     */
+    public function get(array $ids)
+    {
+        $select = $this->tierpriceResource->getConnection()->select()->from($this->tierpriceResource->getMainTable());
+        return $this->tierpriceResource->getConnection()->fetchAll(
+            $select->where($this->getEntityLinkField() . ' IN (?)', $ids)
+        );
+    }
+
+    /**
+     * Update tier prices.
+     *
+     * @param array $prices
+     * @return void
+     * @throws \Magento\Framework\Exception\CouldNotSaveException
+     */
+    public function update(array $prices)
+    {
+        $connection = $this->tierpriceResource->getConnection();
+        $connection->beginTransaction();
+        try {
+            foreach (array_chunk($prices, $this->itemsPerOperation) as $pricesBunch) {
+                $this->tierpriceResource->getConnection()->insertOnDuplicate(
+                    $this->tierpriceResource->getMainTable(),
+                    $pricesBunch,
+                    ['value', 'percentage_value']
+                );
+            }
+            $connection->commit();
+        } catch (\Exception $e) {
+            $connection->rollBack();
+            throw new \Magento\Framework\Exception\CouldNotSaveException(
+                __('Could not save Tier Prices'),
+                $e
+            );
+        }
+    }
+
+    /**
+     * Replace prices.
+     *
+     * @param array $prices
+     * @param array $ids
+     * @return void
+     * @throws \Magento\Framework\Exception\CouldNotSaveException
+     */
+    public function replace(array $prices, array $ids)
+    {
+        $connection = $this->tierpriceResource->getConnection();
+        $connection->beginTransaction();
+        try {
+            foreach (array_chunk($ids, $this->itemsPerOperation) as $idsBunch) {
+                $this->tierpriceResource->getConnection()->delete(
+                    $this->tierpriceResource->getMainTable(),
+                    [$this->getEntityLinkField() . ' IN (?)' => $idsBunch]
+                );
+            }
+
+            foreach (array_chunk($prices, $this->itemsPerOperation) as $pricesBunch) {
+                $this->tierpriceResource->getConnection()->insertMultiple(
+                    $this->tierpriceResource->getMainTable(),
+                    $pricesBunch
+                );
+            }
+            $connection->commit();
+        } catch (\Exception $e) {
+            $connection->rollBack();
+            throw new \Magento\Framework\Exception\CouldNotSaveException(
+                __('Could not replace Tier Prices'),
+                $e
+            );
+        }
+    }
+
+    /**
+     * Delete tier prices by IDs.
+     *
+     * @param array $ids
+     * @return void
+     * @throws \Magento\Framework\Exception\CouldNotDeleteException
+     */
+    public function delete(array $ids)
+    {
+        $connection = $this->tierpriceResource->getConnection();
+        $connection->beginTransaction();
+        try {
+            foreach (array_chunk($ids, $this->itemsPerOperation) as $idsBunch) {
+                $this->tierpriceResource->getConnection()->delete(
+                    $this->tierpriceResource->getMainTable(),
+                    ['value_id IN (?)' => $idsBunch]
+                );
+            }
+            $connection->commit();
+        } catch (\Exception $e) {
+            $connection->rollBack();
+            throw new \Magento\Framework\Exception\CouldNotDeleteException(
+                __('Could not delete Tier Prices'),
+                $e
+            );
+        }
+    }
+
+    /**
+     * Get link field.
+     *
+     * @return string
+     */
+    public function getEntityLinkField()
+    {
+        return $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class)
+            ->getLinkField();
+    }
+}
diff --git a/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php b/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php
new file mode 100644
index 0000000000000000000000000000000000000000..83262bbfa1cca6432109f17ef537c4a039c64c04
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php
@@ -0,0 +1,323 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Model\Product\Price;
+
+use Magento\Catalog\Api\Data\TierPriceInterface;
+
+/**
+ * Tier price storage.
+ */
+class TierPriceStorage implements \Magento\Catalog\Api\TierPriceStorageInterface
+{
+    /**
+     * Tier price resource model.
+     *
+     * @var TierPricePersistence
+     */
+    private $tierPricePersistence;
+
+    /**
+     * Tier price validator.
+     *
+     * @var \Magento\Catalog\Model\Product\Price\TierPriceValidator
+     */
+    private $tierPriceValidator;
+
+    /**
+     * Tier price builder.
+     *
+     * @var TierPriceFactory
+     */
+    private $tierPriceFactory;
+
+    /**
+     * Price indexer.
+     *
+     * @var \Magento\Catalog\Model\Indexer\Product\Price
+     */
+    private $priceIndexer;
+
+    /**
+     * Product ID locator.
+     *
+     * @var \Magento\Catalog\Model\ProductIdLocatorInterface
+     */
+    private $productIdLocator;
+
+    /**
+     * Page cache config.
+     *
+     * @var \Magento\PageCache\Model\Config
+     */
+    private $config;
+
+    /**
+     * Cache type list.
+     *
+     * @var \Magento\Framework\App\Cache\TypeListInterface
+     */
+    private $typeList;
+
+    /**
+     * Indexer chunk value.
+     *
+     * @var int
+     */
+    private $indexerChunkValue = 500;
+
+    /**
+     * TierPriceStorage constructor.
+     *
+     * @param TierPricePersistence $tierPricePersistence
+     * @param TierPriceValidator $tierPriceValidator
+     * @param TierPriceFactory $tierPriceFactory
+     * @param \Magento\Catalog\Model\Indexer\Product\Price $priceIndexer
+     * @param \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator
+     * @param \Magento\PageCache\Model\Config $config
+     * @param \Magento\Framework\App\Cache\TypeListInterface $typeList
+     */
+    public function __construct(
+        TierPricePersistence $tierPricePersistence,
+        TierPriceValidator $tierPriceValidator,
+        TierPriceFactory $tierPriceFactory,
+        \Magento\Catalog\Model\Indexer\Product\Price $priceIndexer,
+        \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator,
+        \Magento\PageCache\Model\Config $config,
+        \Magento\Framework\App\Cache\TypeListInterface $typeList
+    ) {
+        $this->tierPricePersistence = $tierPricePersistence;
+        $this->tierPriceValidator = $tierPriceValidator;
+        $this->tierPriceFactory = $tierPriceFactory;
+        $this->priceIndexer = $priceIndexer;
+        $this->productIdLocator = $productIdLocator;
+        $this->config = $config;
+        $this->typeList = $typeList;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function get(array $skus)
+    {
+        $this->tierPriceValidator->validateSkus($skus);
+        $ids = $this->retrieveAffectedIds($skus);
+        $rawPrices = $this->tierPricePersistence->get($ids);
+        $prices = [];
+
+        foreach ($rawPrices as $rawPrice) {
+            $sku = $this->retrieveSkuById($rawPrice[$this->tierPricePersistence->getEntityLinkField()], $skus);
+            $prices[] = $this->tierPriceFactory->create($rawPrice, $sku);
+        }
+
+        return $prices;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function update(array $prices)
+    {
+        $affectedIds = $this->retrieveAffectedProductIdsForPrices($prices);
+        $skus = array_unique(
+            array_map(function ($price) {
+                return $price->getSku();
+            }, $prices)
+        );
+        $this->tierPriceValidator->validatePrices($prices, $this->get($skus));
+        $formattedPrices = $this->retrieveFormattedPrices($prices);
+        $this->tierPricePersistence->update($formattedPrices);
+        $this->reindexPrices($affectedIds);
+        $this->invalidateFullPageCache();
+
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function replace(array $prices)
+    {
+        $this->tierPriceValidator->validatePrices($prices);
+        $affectedIds = $this->retrieveAffectedProductIdsForPrices($prices);
+        $formattedPrices = $this->retrieveFormattedPrices($prices);
+        $this->tierPricePersistence->replace($formattedPrices, $affectedIds);
+        $this->reindexPrices($affectedIds);
+        $this->invalidateFullPageCache();
+
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function delete(array $prices)
+    {
+        $affectedIds = $this->retrieveAffectedProductIdsForPrices($prices);
+        $this->tierPriceValidator->validatePrices($prices);
+        $priceIds = $this->retrieveAffectedPriceIds($prices);
+        $this->tierPricePersistence->delete($priceIds);
+        $this->reindexPrices($affectedIds);
+        $this->invalidateFullPageCache();
+
+        return true;
+    }
+
+    /**
+     * Retrieve formatted prices.
+     *
+     * @param array $prices
+     * @return array
+     */
+    private function retrieveFormattedPrices(array $prices)
+    {
+        $formattedPrices = [];
+
+        foreach ($prices as $price) {
+            $idsBySku = $this->productIdLocator->retrieveProductIdsBySkus([$price->getSku()]);
+            $ids = array_keys($idsBySku[$price->getSku()]);
+            foreach ($ids as $id) {
+                $formattedPrices[] = $this->tierPriceFactory->createSkeleton($price, $id);
+            }
+        }
+
+        return $formattedPrices;
+    }
+
+    /**
+     * Retrieve affected product IDs for prices.
+     *
+     * @param TierPriceInterface[] $prices
+     * @return array
+     */
+    private function retrieveAffectedProductIdsForPrices(array $prices)
+    {
+        $skus = array_unique(
+            array_map(function ($price) {
+                return $price->getSku();
+            }, $prices)
+        );
+
+        return $this->retrieveAffectedIds($skus);
+    }
+
+    /**
+     * Retrieve affected product IDs.
+     *
+     * @param array $skus
+     * @return array
+     */
+    private function retrieveAffectedIds(array $skus)
+    {
+        $affectedIds = [];
+
+        foreach ($this->productIdLocator->retrieveProductIdsBySkus($skus) as $productId) {
+            $affectedIds = array_merge($affectedIds, array_keys($productId));
+        }
+
+        return array_unique($affectedIds);
+    }
+
+    /**
+     * Retrieve affected price IDs.
+     *
+     * @param array $prices
+     * @return array
+     */
+    private function retrieveAffectedPriceIds(array $prices)
+    {
+        $affectedIds = $this->retrieveAffectedProductIdsForPrices($prices);
+        $formattedPrices = $this->retrieveFormattedPrices($prices);
+        $existingPrices = $this->tierPricePersistence->get($affectedIds);
+        $priceIds = [];
+
+        foreach ($formattedPrices as $price) {
+            $priceIds[] = $this->retrievePriceId($price, $existingPrices);
+        }
+
+        return $priceIds;
+    }
+
+    /**
+     * Retrieve price ID.
+     *
+     * @param array $price
+     * @param array $existingPrices
+     * @return int
+     */
+    private function retrievePriceId(array $price, array $existingPrices)
+    {
+        $linkField = $this->tierPricePersistence->getEntityLinkField();
+
+        foreach ($existingPrices as $existingPrice) {
+            if ($existingPrice['all_groups'] == $price['all_groups']
+                && $existingPrice['customer_group_id'] == $price['customer_group_id']
+                && $existingPrice['qty'] == $price['qty']
+                && $this->isCorrectPriceValue($existingPrice, $price)
+                && $existingPrice[$linkField] == $price[$linkField]
+            ) {
+                return $existingPrice['value_id'];
+            }
+        }
+    }
+
+    /**
+     * Check is correct price value
+     *
+     * @param array $existingPrice
+     * @param array $price
+     * @return bool
+     */
+    private function isCorrectPriceValue(array $existingPrice, array $price)
+    {
+        return ($existingPrice['value'] != 0 && $existingPrice['value'] == $price['value'])
+            || ($existingPrice['percentage_value'] !== null
+                && $existingPrice['percentage_value'] == $price['percentage_value']);
+    }
+
+    /**
+     * Retrieve SKU by product ID.
+     *
+     * @param int $id
+     * @param array $skus
+     * @return int|null
+     */
+    private function retrieveSkuById($id, $skus)
+    {
+        foreach ($this->productIdLocator->retrieveProductIdsBySkus($skus) as $sku => $ids) {
+            if (false !== array_key_exists($id, $ids)) {
+                return $sku;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Invalidate full page cache.
+     *
+     * @return void
+     */
+    private function invalidateFullPageCache()
+    {
+        if ($this->config->isEnabled()) {
+            $this->typeList->invalidate('full_page');
+        }
+    }
+
+    /**
+     * Reindex prices.
+     *
+     * @param array $ids
+     * @return void
+     */
+    private function reindexPrices(array $ids)
+    {
+        foreach (array_chunk($ids, $this->indexerChunkValue) as $affectedIds) {
+            $this->priceIndexer->execute($affectedIds);
+        }
+    }
+}
diff --git a/app/code/Magento/Catalog/Model/Product/Price/TierPriceValidator.php b/app/code/Magento/Catalog/Model/Product/Price/TierPriceValidator.php
new file mode 100644
index 0000000000000000000000000000000000000000..907fd0f66bbdd3d02ff288c141361d56fcfe9694
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/Product/Price/TierPriceValidator.php
@@ -0,0 +1,351 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Model\Product\Price;
+
+use Magento\Catalog\Api\Data\TierPriceInterface;
+
+/**
+ * Tier Price Validator.
+ */
+class TierPriceValidator
+{
+    /**
+     * Groups by code cache.
+     *
+     * @var array
+     */
+    private $customerGroupsByCode = [];
+
+    /**
+     * @var TierPricePersistence
+     */
+    private $tierPricePersistence;
+
+    /**
+     * All groups value.
+     *
+     * @var string
+     */
+    private $allGroupsValue = 'all groups';
+
+    /**
+     * All websites value.
+     *
+     * @var string
+     */
+    private $allWebsitesValue = "0";
+
+    /**
+     * Allowed product types.
+     *
+     * @var array
+     */
+    private $allowedProductTypes = [];
+
+    /**
+     * TierPriceValidator constructor.
+     *
+     * @param \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator
+     * @param \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder
+     * @param \Magento\Framework\Api\FilterBuilder $filterBuilder
+     * @param \Magento\Customer\Api\GroupRepositoryInterface $customerGroupRepository
+     * @param \Magento\Store\Api\WebsiteRepositoryInterface $websiteRepository
+     * @param TierPricePersistence $tierPricePersistence
+     * @param array $allowedProductTypes
+     */
+    public function __construct(
+        \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator,
+        \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder,
+        \Magento\Framework\Api\FilterBuilder $filterBuilder,
+        \Magento\Customer\Api\GroupRepositoryInterface $customerGroupRepository,
+        \Magento\Store\Api\WebsiteRepositoryInterface $websiteRepository,
+        TierPricePersistence $tierPricePersistence,
+        array $allowedProductTypes = []
+    ) {
+        $this->productIdLocator = $productIdLocator;
+        $this->searchCriteriaBuilder = $searchCriteriaBuilder;
+        $this->filterBuilder = $filterBuilder;
+        $this->customerGroupRepository = $customerGroupRepository;
+        $this->websiteRepository = $websiteRepository;
+        $this->tierPricePersistence = $tierPricePersistence;
+        $this->allowedProductTypes = $allowedProductTypes;
+    }
+
+    /**
+     * Validate SKU.
+     *
+     * @param array $skus
+     * @throws \Magento\Framework\Exception\LocalizedException
+     * @return void
+     */
+    public function validateSkus(array $skus)
+    {
+        $idsBySku = $this->productIdLocator->retrieveProductIdsBySkus($skus);
+        $skuDiff = array_diff($skus, array_keys($idsBySku));
+
+        foreach ($idsBySku as $sku => $ids) {
+            foreach (array_values($ids) as $type) {
+                if (!in_array($type, $this->allowedProductTypes)) {
+                    $skuDiff[] = $sku;
+                    break;
+                }
+            }
+        }
+
+        if (!empty($skuDiff)) {
+            $values = implode(', ', $skuDiff);
+            $description = count($skuDiff) == 1
+                ? __('Requested product doesn\'t exist: %1', $values)
+                : __('Requested products don\'t exist: %1', $values);
+            throw new \Magento\Framework\Exception\NoSuchEntityException($description);
+        }
+    }
+
+    /**
+     * Validate that prices have appropriate values and are unique.
+     *
+     * @param array $prices
+     * @param array $existingPrices
+     * @return void
+     * @throws \Magento\Framework\Exception\LocalizedException
+     * @throws \Magento\Framework\Exception\NoSuchEntityException
+     */
+    public function validatePrices(array $prices, array $existingPrices = [])
+    {
+        $skus = array_unique(
+            array_map(function ($price) {
+                if (!$price->getSku()) {
+                    throw new \Magento\Framework\Exception\LocalizedException(
+                        __(
+                            'Invalid attribute %fieldName: %fieldValue.',
+                            [
+                                'fieldName' => 'sku',
+                                'fieldValue' => $price->getSku()
+                            ]
+                        )
+                    );
+                }
+                return $price->getSku();
+            }, $prices)
+        );
+        $this->validateSkus($skus);
+        $idsBySku = $this->productIdLocator->retrieveProductIdsBySkus($skus);
+
+        $pricesBySku = [];
+
+        foreach ($prices as $price) {
+            $pricesBySku[$price->getSku()][] = $price;
+        }
+
+        /** @var TierPriceInterface $price */
+        foreach ($prices as $price) {
+            $this->checkPrice($price);
+            $this->checkPriceType($price, $idsBySku[$price->getSku()]);
+            $this->checkQuantity($price);
+            $this->checkWebsite($price);
+            if (isset($pricesBySku[$price->getSku()])) {
+                $this->checkUnique($price, $pricesBySku[$price->getSku()]);
+            }
+            $this->checkUnique($price, $existingPrices);
+            $this->checkGroup($price);
+        }
+    }
+
+    /**
+     * Verify that price value is correct.
+     *
+     * @param TierPriceInterface $price
+     * @throws \Magento\Framework\Exception\LocalizedException
+     * @return void
+     */
+    private function checkPrice(TierPriceInterface $price)
+    {
+        if (
+            null === $price->getPrice()
+            || $price->getPrice() < 0
+            || ($price->getPriceType() === TierPriceInterface::PRICE_TYPE_DISCOUNT && $price->getPrice() > 100)
+        ) {
+            throw new \Magento\Framework\Exception\LocalizedException(
+                __(
+                    'Invalid attribute %fieldName: %fieldValue.',
+                    [
+                        'fieldName' => 'Price',
+                        'fieldValue' => $price->getPrice()
+                    ]
+                )
+            );
+        }
+    }
+
+    /**
+     * Verify that price type is correct.
+     *
+     * @param TierPriceInterface $price
+     * @param array $ids
+     * @throws \Magento\Framework\Exception\LocalizedException
+     * @return void
+     */
+    private function checkPriceType(TierPriceInterface $price, array $ids)
+    {
+        if (
+            !in_array(
+                $price->getPriceType(),
+                [TierPriceInterface::PRICE_TYPE_FIXED, TierPriceInterface::PRICE_TYPE_DISCOUNT]
+            )
+            || (array_search(\Magento\Catalog\Model\Product\Type::TYPE_BUNDLE, $ids)
+                && $price->getPriceType() !== TierPriceInterface::PRICE_TYPE_DISCOUNT)
+        ) {
+            throw new \Magento\Framework\Exception\LocalizedException(
+                __(
+                    'Invalid attribute %fieldName: %fieldValue.',
+                    [
+                        'fieldName' => 'Price Type',
+                        'fieldValue' => $price->getPriceType()
+                    ]
+                )
+            );
+        }
+    }
+
+    /**
+     * Verify that product quantity is correct.
+     *
+     * @param TierPriceInterface $price
+     * @throws \Magento\Framework\Exception\LocalizedException
+     * @return void
+     */
+    private function checkQuantity(TierPriceInterface $price)
+    {
+        if ($price->getQuantity() < 1) {
+            throw new \Magento\Framework\Exception\LocalizedException(
+                __(
+                    'Invalid attribute %fieldName: %fieldValue.',
+                    [
+                        'fieldName' => 'Quantity',
+                        'fieldValue' => $price->getQuantity()
+                    ]
+                )
+            );
+        }
+    }
+
+    /**
+     * Verify that website exists.
+     *
+     * @param TierPriceInterface $price
+     * @return void
+     * @throws \Magento\Framework\Exception\LocalizedException
+     */
+    private function checkWebsite(TierPriceInterface $price)
+    {
+        try {
+            $this->websiteRepository->getById($price->getWebsiteId());
+        } catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
+            throw new \Magento\Framework\Exception\NoSuchEntityException(
+                __(
+                    'Invalid attribute %fieldName: %fieldValue.',
+                    [
+                        'fieldName' => 'website_id',
+                        'fieldValue' => $price->getWebsiteId()
+                    ]
+                )
+            );
+        }
+    }
+
+    /**
+     * Check website value is unique.
+     *
+     * @param TierPriceInterface $tierPrice
+     * @param array $prices
+     * @return void
+     * @throws \Magento\Framework\Exception\LocalizedException
+     */
+    private function checkUnique(TierPriceInterface $tierPrice, array $prices)
+    {
+        /** @var TierPriceInterface $price */
+        foreach ($prices as $price) {
+            if (
+                $price->getSku() === $tierPrice->getSku()
+                && $price->getCustomerGroup() === $tierPrice->getCustomerGroup()
+                && $price->getQuantity() == $tierPrice->getQuantity()
+                && (
+                    ($price->getWebsiteId() == $this->allWebsitesValue
+                        || $tierPrice->getWebsiteId() == $this->allWebsitesValue)
+                    && $price->getWebsiteId() != $tierPrice->getWebsiteId()
+                )
+            ) {
+                throw new \Magento\Framework\Exception\LocalizedException(
+                    __(
+                        'We found a duplicate website, tier price, customer group and quantity: '
+                        . '%fieldName1 = %fieldValue1, %fieldName2 = %fieldValue2, %fieldName3 = %fieldValue3.',
+                        [
+                            'fieldName1' => 'Customer Group',
+                            'fieldValue1' => $price->getCustomerGroup(),
+                            'fieldName2' => 'Website Id',
+                            'fieldValue2' => $price->getWebsiteId(),
+                            'fieldName3' => 'Quantity',
+                            'fieldValue3' => $price->getQuantity()
+                        ]
+                    )
+                );
+            }
+        }
+    }
+
+    /**
+     * Check customer group exists and has correct value.
+     *
+     * @param TierPriceInterface $price
+     * @throws \Magento\Framework\Exception\NoSuchEntityException
+     * @return void
+     */
+    private function checkGroup(TierPriceInterface $price)
+    {
+        $customerGroup = strtolower($price->getCustomerGroup());
+
+        if ($customerGroup != $this->allGroupsValue) {
+            $this->retrieveGroupValue($customerGroup);
+        }
+    }
+
+    /**
+     * Retrieve customer group id by code.
+     *
+     * @param string $code
+     * @return int
+     * @throws \Magento\Framework\Exception\NoSuchEntityException
+     */
+    private function retrieveGroupValue($code)
+    {
+        if (!isset($this->customerGroupsByCode[$code])) {
+            $searchCriteria = $this->searchCriteriaBuilder->addFilters(
+                [
+                    $this->filterBuilder->setField('customer_group_code')->setValue($code)->create()
+                ]
+            );
+            $items = $this->customerGroupRepository->getList($searchCriteria->create())->getItems();
+            $item = array_shift($items);
+
+            if (!$item) {
+                throw new \Magento\Framework\Exception\NoSuchEntityException(
+                    __(
+                        'No such entity with %fieldName = %fieldValue.',
+                        [
+                            'fieldName' => 'Customer Group',
+                            'fieldValue' => $code
+                        ]
+                    )
+                );
+            }
+
+            $this->customerGroupsByCode[strtolower($item->getCode())] = $item->getId();
+        }
+
+        return $this->customerGroupsByCode[$code];
+    }
+}
diff --git a/app/code/Magento/Catalog/Model/Product/Type/Price.php b/app/code/Magento/Catalog/Model/Product/Type/Price.php
index af15e049203f3f6aae27ef2c6ff071d49cdab8cd..13dfdfe6e5cdc457e751348991fbb806e02ccb12 100644
--- a/app/code/Magento/Catalog/Model/Product/Type/Price.php
+++ b/app/code/Magento/Catalog/Model/Product/Type/Price.php
@@ -414,11 +414,12 @@ class Price
         $prices = [];
         foreach ($tierPrices as $price) {
             $extensionAttributes = $price->getExtensionAttributes();
-            $websiteId = $extensionAttributes && $extensionAttributes->getWebsiteId()
-                ? $extensionAttributes->getWebsiteId()
-                : $websiteId;
+            $priceWebsiteId = $websiteId;
+            if (isset($extensionAttributes) && is_numeric($extensionAttributes->getWebsiteId())) {
+                $priceWebsiteId = (string)$extensionAttributes->getWebsiteId();
+            }
             $prices[] = [
-                'website_id' => $websiteId,
+                'website_id' => $priceWebsiteId,
                 'cust_group' => $price->getCustomerGroupId(),
                 'website_price' => $price->getValue(),
                 'price' => $price->getValue(),
diff --git a/app/code/Magento/Catalog/Model/ProductIdLocator.php b/app/code/Magento/Catalog/Model/ProductIdLocator.php
new file mode 100644
index 0000000000000000000000000000000000000000..1678ecd23c9262229ab063c2ad5af65abad9b49d
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/ProductIdLocator.php
@@ -0,0 +1,100 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Model;
+
+/**
+ * Product ID locator provides all product IDs by SKUs.
+ * @api
+ */
+class ProductIdLocator implements \Magento\Catalog\Model\ProductIdLocatorInterface
+{
+    /**
+     * Search Criteria builder.
+     *
+     * @var \Magento\Framework\Api\SearchCriteriaBuilder
+     */
+    private $searchCriteriaBuilder;
+
+    /**
+     * Filter builder.
+     *
+     * @var \Magento\Framework\Api\FilterBuilder
+     */
+    private $filterBuilder;
+
+    /**
+     * Metadata pool.
+     *
+     * @var \Magento\Framework\EntityManager\MetadataPool
+     */
+    private $metadataPool;
+
+    /**
+     * Product repository.
+     *
+     * @var \Magento\Catalog\Api\ProductRepositoryInterface
+     */
+    private $productRepository;
+
+    /**
+     * IDs by SKU cache.
+     *
+     * @var array
+     */
+    private $idsBySku = [];
+
+    /**
+     * ProductIdLocatorInterface constructor.
+     *
+     * @param \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder
+     * @param \Magento\Framework\Api\FilterBuilder $filterBuilder
+     * @param \Magento\Framework\EntityManager\MetadataPool $metadataPool
+     * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository
+     */
+    public function __construct(
+        \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder,
+        \Magento\Framework\Api\FilterBuilder $filterBuilder,
+        \Magento\Framework\EntityManager\MetadataPool $metadataPool,
+        \Magento\Catalog\Api\ProductRepositoryInterface $productRepository
+    ) {
+        $this->searchCriteriaBuilder = $searchCriteriaBuilder;
+        $this->filterBuilder = $filterBuilder;
+        $this->metadataPool = $metadataPool;
+        $this->productRepository = $productRepository;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function retrieveProductIdsBySkus(array $skus)
+    {
+        $skus = array_map('trim', $skus);
+        $skusInCache = $this->idsBySku ? array_keys($this->idsBySku) : [];
+        $neededSkus = array_diff($skus, $skusInCache);
+
+        if (!empty($neededSkus)) {
+            $searchCriteria = $this->searchCriteriaBuilder->addFilters(
+                [
+                    $this->filterBuilder
+                        ->setField(\Magento\Catalog\Api\Data\ProductInterface::SKU)
+                        ->setConditionType('in')
+                        ->setValue($neededSkus)
+                        ->create(),
+                ]
+            );
+            $items = $this->productRepository->getList($searchCriteria->create())->getItems();
+            $linkField = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class)
+                ->getLinkField();
+
+            foreach ($items as $item) {
+                $this->idsBySku[$item->getSku()][$item->getData($linkField)] = $item->getTypeId();
+            }
+        }
+
+        return array_intersect_key($this->idsBySku, array_flip($skus));
+    }
+}
diff --git a/app/code/Magento/Catalog/Model/ProductIdLocatorInterface.php b/app/code/Magento/Catalog/Model/ProductIdLocatorInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..f9a0d88df2eac54e741478d803891f9eb1214152
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/ProductIdLocatorInterface.php
@@ -0,0 +1,20 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Catalog\Model;
+
+/**
+ * Product ID locator provides all product IDs by SKU.
+ */
+interface ProductIdLocatorInterface
+{
+    /**
+     * Will return associative array of product ids as key and type as value grouped by SKUs.
+     *
+     * @param array $skus
+     * @return array
+     */
+    public function retrieveProductIdsBySkus(array $skus);
+}
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/BasePriceStorageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/BasePriceStorageTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..ef99e550cdc21ad00f9a726d149528af24ce59c5
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/BasePriceStorageTest.php
@@ -0,0 +1,326 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Test\Unit\Model\Product\Price;
+
+/**
+ * Class BasePriceStorageTest.
+ */
+class BasePriceStorageTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\PricePersistenceFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $pricePersistenceFactory;
+
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\PricePersistence|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $pricePersistence;
+
+    /**
+     * @var \Magento\Catalog\Api\Data\BasePriceInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $basePriceInterfaceFactory;
+
+    /**
+     * @var \Magento\Catalog\Api\Data\BasePriceInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $basePriceInterface;
+
+    /**
+     * @var \Magento\Catalog\Model\ProductIdLocatorInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $productIdLocator;
+
+    /**
+     * @var \Magento\Store\Api\StoreRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $storeRepository;
+
+    /**
+     * @var \Magento\Catalog\Api\ProductRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $productRepository;
+
+    /**
+     * @var \Magento\Catalog\Api\Data\ProductInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $product;
+
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\BasePriceStorage
+     */
+    private $model;
+
+    /**
+     * Set up.
+     *
+     * @return void
+     */
+    protected function setUp()
+    {
+        $this->pricePersistenceFactory = $this->getMock(
+            \Magento\Catalog\Model\Product\Price\PricePersistenceFactory::class,
+            ['create'],
+            [],
+            '',
+            false
+        );
+        $this->pricePersistence = $this->getMock(
+            \Magento\Catalog\Model\Product\Price\PricePersistence::class,
+            ['get', 'retrieveSkuById', 'update', 'getEntityLinkField'],
+            [],
+            '',
+            false
+        );
+        $this->basePriceInterfaceFactory = $this->getMock(
+            \Magento\Catalog\Api\Data\BasePriceInterfaceFactory::class,
+            ['create'],
+            [],
+            '',
+            false
+        );
+        $this->basePriceInterface = $this->getMockForAbstractClass(
+            \Magento\Catalog\Api\Data\BasePriceInterface::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            ['setSku', 'setPrice', 'setStoreId', 'getSku', 'getPrice', 'getStoreId']
+        );
+        $this->productIdLocator = $this->getMockForAbstractClass(
+            \Magento\Catalog\Model\ProductIdLocatorInterface::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            ['retrieveProductIdsBySkus']
+        );
+        $this->storeRepository = $this->getMockForAbstractClass(
+            \Magento\Store\Api\StoreRepositoryInterface::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            ['getById']
+        );
+        $this->productRepository = $this->getMockForAbstractClass(
+            \Magento\Catalog\Api\ProductRepositoryInterface::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            ['get']
+        );
+        $this->product = $this->getMockForAbstractClass(
+            \Magento\Catalog\Api\Data\ProductInterface::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            ['getPriceType']
+        );
+
+        $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+        $this->model = $objectManager->getObject(
+            \Magento\Catalog\Model\Product\Price\BasePriceStorage::class,
+            [
+                'pricePersistenceFactory' => $this->pricePersistenceFactory,
+                'basePriceInterfaceFactory' => $this->basePriceInterfaceFactory,
+                'productIdLocator' => $this->productIdLocator,
+                'storeRepository' => $this->storeRepository,
+                'productRepository' => $this->productRepository,
+                'allowedProductTypes' => ['simple', 'virtual', 'bundle', 'downloadable'],
+            ]
+        );
+    }
+
+    /**
+     * Test get method.
+     *
+     * @return void
+     */
+    public function testGet()
+    {
+        $skus = ['sku_1', 'sku_2'];
+        $idsBySku = [
+            'sku_1' =>
+                [
+                    1 => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE
+                ],
+            'sku_2' =>
+                [
+                    2 => \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE
+                ]
+        ];
+        $rawPrices = [
+            [
+                'row_id' => 1,
+                'value' => 15,
+                'store_id' => 1
+            ],
+            [
+                'row_id' => 2,
+                'value' => 35,
+                'store_id' => 1
+            ]
+        ];
+        $this->productIdLocator
+            ->expects($this->once())
+            ->method('retrieveProductIdsBySkus')->with($skus)
+            ->willReturn($idsBySku);
+        $this->productRepository->expects($this->once())->method('get')->willReturn($this->product);
+        $this->product->expects($this->once())->method('getPriceType')->willReturn(1);
+        $this->pricePersistenceFactory
+            ->expects($this->once())
+            ->method('create')
+            ->with(['attributeCode' => 'price'])
+            ->willReturn($this->pricePersistence);
+        $this->pricePersistence->expects($this->once())->method('get')->with($skus)->willReturn($rawPrices);
+        $this->pricePersistence->expects($this->atLeastOnce())->method('getEntityLinkField')->willReturn('row_id');
+        $this->basePriceInterfaceFactory
+            ->expects($this->exactly(2))
+            ->method('create')
+            ->willReturn($this->basePriceInterface);
+        $this->pricePersistence
+            ->expects($this->exactly(2))
+            ->method('retrieveSkuById')
+            ->willReturnOnConsecutiveCalls('sku_1', 'sku_2');
+        $this->basePriceInterface
+            ->expects($this->exactly(2))
+            ->method('setSku')
+            ->withConsecutive(['sku_1'], ['sku_2'])
+            ->willReturnSelf();
+        $this->basePriceInterface
+            ->expects($this->exactly(2))
+            ->method('setPrice')
+            ->withConsecutive([15], [35])
+            ->willReturnSelf();
+        $this->basePriceInterface
+            ->expects($this->exactly(2))
+            ->method('setStoreId')
+            ->withConsecutive([1], [1])
+            ->willReturnSelf();
+        $this->model->get($skus);
+    }
+
+    /**
+     * Test get method with exception.
+     *
+     * @expectedException \Magento\Framework\Exception\NoSuchEntityException
+     * @expectedExceptionMessage Requested products don't exist: sku_1, sku_2
+     */
+    public function testGetWithException()
+    {
+        $skus = ['sku_1', 'sku_2'];
+        $idsBySku = [
+            'sku_1' =>
+                [
+                    1 => 'configurable'
+                ],
+            'sku_2' =>
+                [
+                    2 => 'grouped'
+                ]
+        ];
+        $this->productIdLocator
+            ->expects($this->once())
+            ->method('retrieveProductIdsBySkus')->with($skus)
+            ->willReturn($idsBySku);
+        $this->model->get($skus);
+    }
+
+    /**
+     * Test update method.
+     *
+     * @return void
+     */
+    public function testUpdate()
+    {
+        $store = $this->getMockForAbstractClass(
+            \Magento\Store\Api\Data\StoreInterface::class,
+            [],
+            '',
+            false
+        );
+        $sku = 'sku_1';
+        $idsBySku = [
+            'sku_1' =>
+                [
+                    1 => \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE
+                ]
+        ];
+        $this->basePriceInterface->expects($this->exactly(4))->method('getSku')->willReturn($sku);
+        $this->productIdLocator
+            ->expects($this->exactly(2))
+            ->method('retrieveProductIdsBySkus')->with([$sku])
+            ->willReturn($idsBySku);
+        $this->productRepository->expects($this->once())->method('get')->willReturn($this->product);
+        $this->product->expects($this->once())->method('getPriceType')->willReturn(1);
+        $this->basePriceInterface->expects($this->exactly(3))->method('getPrice')->willReturn(15);
+        $this->basePriceInterface->expects($this->exactly(2))->method('getStoreId')->willReturn(1);
+        $this->pricePersistence->expects($this->atLeastOnce())->method('getEntityLinkField')->willReturn('row_id');
+        $this->storeRepository->expects($this->once())->method('getById')->with(1)->willReturn($store);
+        $this->pricePersistenceFactory
+            ->expects($this->once())
+            ->method('create')
+            ->with(['attributeCode' => 'price'])
+            ->willReturn($this->pricePersistence);
+        $formattedPrices = [
+            [
+                'store_id' => 1,
+                'row_id' => 1,
+                'value' => 15
+            ]
+        ];
+        $this->pricePersistence->expects($this->once())->method('update')->with($formattedPrices);
+        $this->assertTrue($this->model->update([$this->basePriceInterface]));
+    }
+
+    /**
+     * Test update method without SKU.
+     *
+     * @expectedException \Magento\Framework\Exception\LocalizedException
+     * @expectedExceptionMessage Invalid attribute sku: .
+     */
+    public function testUpdateWithoutSku()
+    {
+        $this->basePriceInterface->expects($this->exactly(2))->method('getSku')->willReturn(null);
+        $this->model->update([$this->basePriceInterface]);
+    }
+
+    /**
+     * Test update method with negative price.
+     *
+     * @expectedException \Magento\Framework\Exception\LocalizedException
+     * @expectedExceptionMessage Invalid attribute Price: -15.
+     */
+    public function testUpdateWithNegativePrice()
+    {
+        $sku = 'sku_1';
+        $idsBySku = [
+            'sku_1' =>
+                [
+                    1 => \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE
+                ]
+        ];
+        $this->basePriceInterface->expects($this->exactly(2))->method('getSku')->willReturn($sku);
+        $this->productIdLocator
+            ->expects($this->once(1))
+            ->method('retrieveProductIdsBySkus')->with([$sku])
+            ->willReturn($idsBySku);
+        $this->productRepository->expects($this->once())->method('get')->willReturn($this->product);
+        $this->product->expects($this->once())->method('getPriceType')->willReturn(1);
+        $this->basePriceInterface->expects($this->exactly(3))->method('getPrice')->willReturn(-15);
+        $this->model->update([$this->basePriceInterface]);
+    }
+}
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/CostStorageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/CostStorageTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..b1dea66928f1bd98788758420167bde6dcca2329
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/CostStorageTest.php
@@ -0,0 +1,322 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Test\Unit\Model\Product\Price;
+
+/**
+ * Class CostStorageTest.
+ */
+class CostStorageTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\PricePersistenceFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $pricePersistenceFactory;
+
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\PricePersistence|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $pricePersistence;
+
+    /**
+     * @var \Magento\Catalog\Api\Data\CostInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $costInterfaceFactory;
+
+    /**
+     * @var \Magento\Catalog\Api\Data\CostInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $costInterface;
+
+    /**
+     * @var \Magento\Catalog\Model\ProductIdLocatorInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $productIdLocator;
+
+    /**
+     * @var \Magento\Store\Api\StoreRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $storeRepository;
+
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\CostStorage
+     */
+    private $model;
+
+    /**
+     * Set up.
+     *
+     * @return void
+     */
+    protected function setUp()
+    {
+        $this->pricePersistenceFactory = $this->getMock(
+            \Magento\Catalog\Model\Product\Price\PricePersistenceFactory::class,
+            ['create'],
+            [],
+            '',
+            false
+        );
+        $this->pricePersistence = $this->getMock(
+            \Magento\Catalog\Model\Product\Price\PricePersistence::class,
+            ['get', 'retrieveSkuById', 'update', 'delete', 'getEntityLinkField'],
+            [],
+            '',
+            false
+        );
+        $this->costInterfaceFactory = $this->getMock(
+            \Magento\Catalog\Api\Data\CostInterfaceFactory::class,
+            ['create'],
+            [],
+            '',
+            false
+        );
+        $this->costInterface = $this->getMockForAbstractClass(
+            \Magento\Catalog\Api\Data\CostInterface::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            ['setSku', 'setCost', 'setStoreId', 'getSku', 'getCost', 'getStoreId']
+        );
+        $this->productIdLocator = $this->getMockForAbstractClass(
+            \Magento\Catalog\Model\ProductIdLocatorInterface::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            ['retrieveProductIdsBySkus']
+        );
+        $this->storeRepository = $this->getMockForAbstractClass(
+            \Magento\Store\Api\StoreRepositoryInterface::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            ['getById']
+        );
+
+        $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+        $this->model = $objectManager->getObject(
+            \Magento\Catalog\Model\Product\Price\CostStorage::class,
+            [
+                'pricePersistenceFactory' => $this->pricePersistenceFactory,
+                'costInterfaceFactory' => $this->costInterfaceFactory,
+                'productIdLocator' => $this->productIdLocator,
+                'storeRepository' => $this->storeRepository,
+                'allowedProductTypes' => ['simple', 'virtual', 'downloadable'],
+            ]
+        );
+    }
+
+    /**
+     * Test get method.
+     *
+     * @return void
+     */
+    public function testGet()
+    {
+        $skus = ['sku_1', 'sku_2'];
+        $idsBySku = [
+            'sku_1' =>
+                [
+                    1 => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE
+                ],
+            'sku_2' =>
+                [
+                    2 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL
+                ]
+        ];
+        $rawPrices = [
+            [
+                'row_id' => 1,
+                'value' => 15,
+                'store_id' => 1
+            ],
+            [
+                'row_id' => 2,
+                'value' => 35,
+                'store_id' => 1
+            ]
+        ];
+        $this->productIdLocator
+            ->expects($this->once())
+            ->method('retrieveProductIdsBySkus')->with($skus)
+            ->willReturn($idsBySku);
+        $this->pricePersistenceFactory
+            ->expects($this->once())
+            ->method('create')
+            ->with(['attributeCode' => 'cost'])
+            ->willReturn($this->pricePersistence);
+        $this->pricePersistence->expects($this->once())->method('get')->with($skus)->willReturn($rawPrices);
+        $this->costInterfaceFactory
+            ->expects($this->exactly(2))
+            ->method('create')
+            ->willReturn($this->costInterface);
+        $this->pricePersistence
+            ->expects($this->exactly(2))
+            ->method('retrieveSkuById')
+            ->willReturnOnConsecutiveCalls('sku_1', 'sku_2');
+        $this->pricePersistence->expects($this->atLeastOnce())->method('getEntityLinkField')->willReturn('row_id');
+        $this->costInterface
+            ->expects($this->exactly(2))
+            ->method('setSku')
+            ->withConsecutive(['sku_1'], ['sku_2'])
+            ->willReturnSelf();
+        $this->costInterface
+            ->expects($this->exactly(2))
+            ->method('setCost')
+            ->withConsecutive([15], [35])
+            ->willReturnSelf();
+        $this->costInterface
+            ->expects($this->exactly(2))
+            ->method('setStoreId')
+            ->withConsecutive([1], [1])
+            ->willReturnSelf();
+        $this->model->get($skus);
+    }
+
+    /**
+     * Test get method with exception.
+     *
+     * @expectedException \Magento\Framework\Exception\NoSuchEntityException
+     * @expectedExceptionMessage Requested products don't exist: sku_1, sku_2
+     */
+    public function testGetWithException()
+    {
+        $skus = ['sku_1', 'sku_2'];
+        $idsBySku = [
+            'sku_1' =>
+                [
+                    1 => 'configurable'
+                ],
+            'sku_2' =>
+                [
+                    2 => 'grouped'
+                ]
+        ];
+        $this->productIdLocator
+            ->expects($this->once())
+            ->method('retrieveProductIdsBySkus')->with($skus)
+            ->willReturn($idsBySku);
+        $this->model->get($skus);
+    }
+
+    /**
+     * Test update method.
+     *
+     * @return void
+     */
+    public function testUpdate()
+    {
+        $store = $this->getMockForAbstractClass(
+            \Magento\Store\Api\Data\StoreInterface::class,
+            [],
+            '',
+            false
+        );
+        $sku = 'sku_1';
+        $idsBySku = [
+            'sku_1' =>
+                [
+                    1 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL
+                ]
+        ];
+        $this->costInterface->expects($this->exactly(4))->method('getSku')->willReturn($sku);
+        $this->productIdLocator
+            ->expects($this->exactly(2))
+            ->method('retrieveProductIdsBySkus')->with([$sku])
+            ->willReturn($idsBySku);
+        $this->costInterface->expects($this->exactly(3))->method('getCost')->willReturn(15);
+        $this->costInterface->expects($this->exactly(2))->method('getStoreId')->willReturn(1);
+        $this->pricePersistence->expects($this->atLeastOnce())->method('getEntityLinkField')->willReturn('row_id');
+        $this->storeRepository->expects($this->once())->method('getById')->with(1)->willReturn($store);
+        $this->pricePersistenceFactory
+            ->expects($this->once())
+            ->method('create')
+            ->with(['attributeCode' => 'cost'])
+            ->willReturn($this->pricePersistence);
+        $formattedPrices = [
+            [
+                'store_id' => 1,
+                'row_id' => 1,
+                'value' => 15
+            ]
+        ];
+        $this->pricePersistence->expects($this->once())->method('update')->with($formattedPrices);
+        $this->assertTrue($this->model->update([$this->costInterface]));
+    }
+
+    /**
+     * Test update method without SKU.
+     *
+     * @expectedException \Magento\Framework\Exception\LocalizedException
+     * @expectedExceptionMessage Invalid attribute sku: .
+     */
+    public function testUpdateWithoutSku()
+    {
+        $this->costInterface->expects($this->exactly(2))->method('getSku')->willReturn(null);
+        $this->model->update([$this->costInterface]);
+    }
+
+    /**
+     * Test update method with negative cost.
+     *
+     * @expectedException \Magento\Framework\Exception\LocalizedException
+     * @expectedExceptionMessage Invalid attribute Cost: -15.
+     */
+    public function testUpdateWithNegativeCost()
+    {
+        $sku = 'sku_1';
+        $idsBySku = [
+            'sku_1' =>
+                [
+                    1 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL
+                ]
+        ];
+        $this->costInterface->expects($this->exactly(2))->method('getSku')->willReturn($sku);
+        $this->productIdLocator
+            ->expects($this->once(1))
+            ->method('retrieveProductIdsBySkus')->with([$sku])
+            ->willReturn($idsBySku);
+        $this->costInterface->expects($this->exactly(3))->method('getCost')->willReturn(-15);
+        $this->model->update([$this->costInterface]);
+    }
+
+    /**
+     * Test delete method.
+     *
+     * @return void
+     */
+    public function testDelete()
+    {
+        $skus = ['sku_1', 'sku_2'];
+        $idsBySku = [
+            'sku_1' =>
+                [
+                    1 => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE
+                ],
+            'sku_2' =>
+                [
+                    2 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL
+                ]
+        ];
+        $this->productIdLocator
+            ->expects($this->once())
+            ->method('retrieveProductIdsBySkus')->with($skus)
+            ->willReturn($idsBySku);
+        $this->pricePersistenceFactory
+            ->expects($this->once())
+            ->method('create')
+            ->with(['attributeCode' => 'cost'])
+            ->willReturn($this->pricePersistence);
+        $this->pricePersistence->expects($this->once())->method('delete')->with($skus);
+        $this->model->delete($skus);
+    }
+}
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/PricePersistenceTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/PricePersistenceTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..e9f422245d95967905b577c7cc39626007b16d49
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/PricePersistenceTest.php
@@ -0,0 +1,402 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Test\Unit\Model\Product\Price;
+
+/**
+ * Class PricePersistenceTest.
+ */
+class PricePersistenceTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\Catalog\Model\ResourceModel\Attribute|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $attributeResource;
+
+    /**
+     * @var \Magento\Catalog\Api\ProductAttributeRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $attributeRepository;
+
+    /**
+     * @var \Magento\Catalog\Api\Data\ProductAttributeInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $productAttribute;
+
+    /**
+     * @var \Magento\Catalog\Model\ProductIdLocatorInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $productIdLocator;
+
+    /**
+     * @var \Magento\Framework\DB\Adapter\AdapterInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $connection;
+
+    /**
+     * @var \Magento\Framework\EntityManager\MetadataPool|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $metadataPool;
+
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\PricePersistence
+     */
+    private $model;
+
+    /**
+     * Set up.
+     *
+     * @return void
+     */
+    protected function setUp()
+    {
+        $this->attributeResource = $this->getMock(
+            \Magento\Catalog\Model\ResourceModel\Attribute::class,
+            ['getConnection', 'getTable'],
+            [],
+            '',
+            false
+        );
+        $this->attributeRepository = $this->getMockForAbstractClass(
+            \Magento\Catalog\Api\ProductAttributeRepositoryInterface::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            ['get']
+        );
+        $this->productIdLocator = $this->getMockForAbstractClass(
+            \Magento\Catalog\Model\ProductIdLocatorInterface::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            ['retrieveProductIdsBySkus']
+        );
+        $this->metadataPool = $this->getMock(
+            \Magento\Framework\EntityManager\MetadataPool::class,
+            ['getLinkField', 'getMetadata'],
+            [],
+            '',
+            false
+        );
+        $this->connection = $this->getMockForAbstractClass(
+            \Magento\Framework\DB\Adapter\AdapterInterface::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            ['select', 'fetchAll', 'beginTransaction', 'insertOnDuplicate', 'commit', 'rollBack', 'delete']
+        );
+        $this->productAttribute = $this->getMockForAbstractClass(
+            \Magento\Catalog\Api\Data\ProductAttributeInterface::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            ['getAttributeId']
+        );
+
+        $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+        $this->model = $objectManager->getObject(
+            \Magento\Catalog\Model\Product\Price\PricePersistence::class,
+            [
+                'attributeResource' => $this->attributeResource,
+                'attributeRepository' => $this->attributeRepository,
+                'productIdLocator' => $this->productIdLocator,
+                'metadataPool' => $this->metadataPool,
+            ]
+        );
+    }
+
+    /**
+     * Test get method.
+     *
+     * @return void
+     */
+    public function testGet()
+    {
+        $attributeId = 5;
+        $skus = ['sku_1', 'sku_2'];
+        $idsBySku = [
+            'sku_1' =>
+                [
+                    1 => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE
+                ],
+            'sku_2' =>
+                [
+                    2 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL
+                ]
+        ];
+        $select = $this->getMock(
+            \Magento\Framework\DB\Select::class,
+            ['from', 'where'],
+            [],
+            '',
+            false
+        );
+        $this->productIdLocator
+            ->expects($this->once())
+            ->method('retrieveProductIdsBySkus')->with($skus)
+            ->willReturn($idsBySku);
+        $this->attributeResource->expects($this->exactly(2))->method('getConnection')->willReturn($this->connection);
+        $this->connection->expects($this->once())->method('select')->willReturn($select);
+        $this->attributeResource
+            ->expects($this->once())
+            ->method('getTable')
+            ->with('catalog_product_entity_decimal')
+            ->willReturn('catalog_product_entity_decimal');
+        $select->expects($this->once())->method('from')->with('catalog_product_entity_decimal')->willReturnSelf();
+        $this->attributeRepository->expects($this->once())->method('get')->willReturn($this->productAttribute);
+        $this->productAttribute->expects($this->once())->method('getAttributeId')->willReturn($attributeId);
+        $select
+            ->expects($this->exactly(2))
+            ->method('where')
+            ->withConsecutive(['row_id IN (?)', [1, 2]], ['attribute_id = ?', $attributeId])
+            ->willReturnSelf();
+        $this->metadataPool->expects($this->atLeastOnce())->method('getMetadata')->willReturnSelf();
+        $this->metadataPool->expects($this->atLeastOnce())->method('getLinkField')->willReturn('row_id');
+        $this->model->get($skus);
+    }
+
+    /**
+     * Test update method.
+     *
+     * @return void
+     */
+    public function testUpdate()
+    {
+        $attributeId = 5;
+        $prices = [
+            [
+                'store_id' => 1,
+                'row_id' => 1,
+                'value' => 15
+            ]
+        ];
+        $this->attributeRepository->expects($this->once())->method('get')->willReturn($this->productAttribute);
+        $this->productAttribute->expects($this->once())->method('getAttributeId')->willReturn($attributeId);
+        $this->attributeResource->expects($this->exactly(2))->method('getConnection')->willReturn($this->connection);
+        $this->connection->expects($this->once())->method('beginTransaction')->willReturnSelf();
+        $this->attributeResource
+            ->expects($this->once())
+            ->method('getTable')
+            ->with('catalog_product_entity_decimal')
+            ->willReturn('catalog_product_entity_decimal');
+        $this->connection
+            ->expects($this->once())
+            ->method('insertOnDuplicate')
+            ->with(
+                'catalog_product_entity_decimal',
+                [
+                    [
+                        'store_id' => 1,
+                        'row_id' => 1,
+                        'value' => 15,
+                        'attribute_id' => 5,
+                    ]
+                ],
+                ['value']
+            )
+            ->willReturnSelf();
+        $this->connection->expects($this->once())->method('commit')->willReturnSelf();
+        $this->model->update($prices);
+    }
+
+    /**
+     * Test update method throws exception.
+     *
+     * @expectedException \Magento\Framework\Exception\CouldNotSaveException
+     * @expectedExceptionMessage Could not save Prices.
+     */
+    public function testUpdateWithException()
+    {
+        $attributeId = 5;
+        $prices = [
+            [
+                'store_id' => 1,
+                'row_id' => 1,
+                'value' => 15
+            ]
+        ];
+        $this->attributeRepository->expects($this->once())->method('get')->willReturn($this->productAttribute);
+        $this->productAttribute->expects($this->once())->method('getAttributeId')->willReturn($attributeId);
+        $this->attributeResource->expects($this->exactly(2))->method('getConnection')->willReturn($this->connection);
+        $this->connection->expects($this->once())->method('beginTransaction')->willReturnSelf();
+        $this->attributeResource
+            ->expects($this->once())
+            ->method('getTable')
+            ->with('catalog_product_entity_decimal')
+            ->willReturn('catalog_product_entity_decimal');
+        $this->connection
+            ->expects($this->once())
+            ->method('insertOnDuplicate')
+            ->with(
+                'catalog_product_entity_decimal',
+                [
+                    [
+                        'store_id' => 1,
+                        'row_id' => 1,
+                        'value' => 15,
+                        'attribute_id' => 5,
+                    ]
+                ],
+                ['value']
+            )
+            ->willReturnSelf();
+        $this->connection->expects($this->once())->method('commit')->willThrowException(new \Exception());
+        $this->connection->expects($this->once())->method('rollback')->willReturnSelf();
+        $this->model->update($prices);
+    }
+
+    /**
+     * Test delete method.
+     *
+     * @return void
+     */
+    public function testDelete()
+    {
+        $attributeId = 5;
+        $skus = ['sku_1', 'sku_2'];
+        $idsBySku = [
+            'sku_1' =>
+                [
+                    1 => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE
+                ],
+            'sku_2' =>
+                [
+                    2 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL
+                ]
+        ];
+        $this->productIdLocator
+            ->expects($this->once())
+            ->method('retrieveProductIdsBySkus')->with($skus)
+            ->willReturn($idsBySku);
+        $this->attributeRepository->expects($this->once())->method('get')->willReturn($this->productAttribute);
+        $this->productAttribute->expects($this->once())->method('getAttributeId')->willReturn($attributeId);
+        $this->attributeResource->expects($this->exactly(2))->method('getConnection')->willReturn($this->connection);
+        $this->connection->expects($this->once())->method('beginTransaction')->willReturnSelf();
+        $this->attributeResource
+            ->expects($this->once())
+            ->method('getTable')
+            ->with('catalog_product_entity_decimal')
+            ->willReturn('catalog_product_entity_decimal');
+        $this->connection
+            ->expects($this->once())
+            ->method('delete')
+            ->with(
+                'catalog_product_entity_decimal',
+                [
+                    'attribute_id = ?' => $attributeId,
+                    'row_id IN (?)' => [1, 2]
+                ]
+            )
+            ->willReturnSelf();
+        $this->connection->expects($this->once())->method('commit')->willReturnSelf();
+        $this->metadataPool->expects($this->atLeastOnce())->method('getMetadata')->willReturnSelf();
+        $this->metadataPool->expects($this->atLeastOnce())->method('getLinkField')->willReturn('row_id');
+        $this->model->delete($skus);
+    }
+
+    /**
+     * Test delete method throws exception.
+     *
+     * @expectedException \Magento\Framework\Exception\CouldNotDeleteException
+     * @expectedExceptionMessage Could not delete Prices
+     */
+    public function testDeleteWithException()
+    {
+        $attributeId = 5;
+        $skus = ['sku_1', 'sku_2'];
+        $idsBySku = [
+            'sku_1' =>
+                [
+                    1 => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE
+                ],
+            'sku_2' =>
+                [
+                    2 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL
+                ]
+        ];
+        $this->productIdLocator
+            ->expects($this->once())
+            ->method('retrieveProductIdsBySkus')->with($skus)
+            ->willReturn($idsBySku);
+        $this->attributeRepository->expects($this->once())->method('get')->willReturn($this->productAttribute);
+        $this->productAttribute->expects($this->once())->method('getAttributeId')->willReturn($attributeId);
+        $this->attributeResource->expects($this->exactly(2))->method('getConnection')->willReturn($this->connection);
+        $this->connection->expects($this->once())->method('beginTransaction')->willReturnSelf();
+        $this->attributeResource
+            ->expects($this->once())
+            ->method('getTable')
+            ->with('catalog_product_entity_decimal')
+            ->willReturn('catalog_product_entity_decimal');
+        $this->connection
+            ->expects($this->once())
+            ->method('delete')
+            ->with(
+                'catalog_product_entity_decimal',
+                [
+                    'attribute_id = ?' => $attributeId,
+                    'row_id IN (?)' => [1, 2]
+                ]
+            )
+            ->willReturnSelf();
+        $this->connection->expects($this->once())->method('commit')->willThrowException(new \Exception());
+        $this->connection->expects($this->once())->method('rollBack')->willReturnSelf();
+        $this->metadataPool->expects($this->atLeastOnce())->method('getMetadata')->willReturnSelf();
+        $this->metadataPool->expects($this->atLeastOnce())->method('getLinkField')->willReturn('row_id');
+        $this->model->delete($skus);
+    }
+
+    /**
+     * Test retrieveSkuById method.
+     *
+     * @param int|null $expectedResult
+     * @param int $id
+     * @param array $skus
+     * @dataProvider dataProviderRetrieveSkuById
+     */
+    public function testRetrieveSkuById($expectedResult, $id, array $skus)
+    {
+        $this->productIdLocator
+            ->expects($this->once())
+            ->method('retrieveProductIdsBySkus')
+            ->willReturn($skus);
+
+        $this->assertEquals($expectedResult, $this->model->retrieveSkuById($id, $skus));
+    }
+
+    /**
+     * Data provider for retrieveSkuById  method.
+     *
+     * @return array
+     */
+    public function dataProviderRetrieveSkuById()
+    {
+        return [
+            [
+                null,
+                2,
+                ['sku_1' => [1 => 1]]
+            ],
+            [
+                'sku_1',
+                1,
+                ['sku_1' => [1 => 1]]
+            ],
+            [
+                null,
+                1,
+                ['sku_1' => [2 => 1]]
+            ],
+        ];
+    }
+}
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceStorageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceStorageTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..2a885dd8b83698bffcb0106992bee076692bffca
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceStorageTest.php
@@ -0,0 +1,292 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Test\Unit\Model\Product\Price;
+
+/**
+ * TierPriceStorage test.
+ */
+class TierPriceStorageTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\TierPricePersistence|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $tierPricePersistence;
+
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\TierPriceValidator|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $tierPriceValidator;
+
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\TierPriceFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $tierPriceFactory;
+
+    /**
+     * @var \Magento\Catalog\Model\Indexer\Product\Price|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $priceIndexer;
+
+    /**
+     * @var \Magento\Catalog\Model\ProductIdLocatorInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $productIdLocator;
+
+    /**
+     * @var \Magento\PageCache\Model\Config|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $config;
+
+    /**
+     * @var \Magento\Framework\App\Cache\TypeListInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $typeList;
+
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\TierPriceStorage
+     */
+    private $tierPriceStorage;
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function setUp()
+    {
+        $this->tierPricePersistence = $this->getMock(
+            \Magento\Catalog\Model\Product\Price\TierPricePersistence::class,
+            [],
+            [],
+            '',
+            false
+        );
+        $this->tierPricePersistence->expects($this->any())
+            ->method('getEntityLinkField')
+            ->willReturn('row_id');
+        $this->tierPriceValidator = $this->getMock(
+            \Magento\Catalog\Model\Product\Price\TierPriceValidator::class,
+            [],
+            [],
+            '',
+            false
+        );
+        $this->tierPriceFactory = $this->getMock(
+            \Magento\Catalog\Model\Product\Price\TierPriceFactory::class,
+            [],
+            [],
+            '',
+            false
+        );
+        $this->priceIndexer = $this->getMock(
+            \Magento\Catalog\Model\Indexer\Product\Price::class,
+            [],
+            [],
+            '',
+            false
+        );
+        $this->productIdLocator = $this->getMock(
+            \Magento\Catalog\Model\ProductIdLocatorInterface::class,
+            [],
+            [],
+            '',
+            false
+        );
+        $this->config = $this->getMock(
+            \Magento\PageCache\Model\Config::class,
+            [],
+            [],
+            '',
+            false
+        );
+        $this->typeList = $this->getMock(
+            \Magento\Framework\App\Cache\TypeListInterface::class,
+            [],
+            [],
+            '',
+            false
+        );
+        $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+        $this->tierPriceStorage = $objectManager->getObject(
+            \Magento\Catalog\Model\Product\Price\TierPriceStorage::class,
+            [
+                'tierPricePersistence' => $this->tierPricePersistence,
+                'tierPriceValidator' => $this->tierPriceValidator,
+                'tierPriceFactory' => $this->tierPriceFactory,
+                'priceIndexer' => $this->priceIndexer,
+                'productIdLocator' => $this->productIdLocator,
+                'config' => $this->config,
+                'typeList' => $this->typeList,
+            ]
+        );
+    }
+
+    /**
+     * Test get method.
+     * @return void
+     */
+    public function testGet()
+    {
+        $skus = ['simple', 'virtual'];
+        $this->productIdLocator->expects($this->atLeastOnce())
+            ->method('retrieveProductIdsBySkus')
+            ->with(['simple', 'virtual'])
+            ->willReturn(['simple' => ['2' => 'simple'], 'virtual' => ['3' => 'virtual']]);
+        $this->tierPricePersistence->expects($this->once())
+            ->method('get')
+            ->willReturn(
+                [
+                    [
+                        'value_id' => 1,
+                        'row_id' => 2,
+                        'all_groups' => 1,
+                        'customer_group_id' => 0,
+                        'qty' => 2.0000,
+                        'value' => 2.0000,
+                        'percentage_value' => null,
+                        'website_id' => 0
+                    ],
+                    [
+                        'value_id' => 2,
+                        'row_id' => 3,
+                        'all_groups' => 1,
+                        'customer_group_id' => 0,
+                        'qty' => 3.0000,
+                        'value' => 3.0000,
+                        'percentage_value' => null,
+                        'website_id' => 0
+                    ]
+                ]
+            );
+        $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\TierPriceInterface::class)->getMockForAbstractClass();
+        $this->tierPriceFactory->expects($this->at(0))->method('create')->willReturn($price);
+        $this->tierPriceFactory->expects($this->at(1))->method('create')->willReturn($price);
+        $prices = $this->tierPriceStorage->get($skus);
+        $this->assertNotEmpty($prices);
+        $this->assertEquals(2, count($prices));
+    }
+
+    /**
+     * Test update method.
+     * @return void
+     */
+    public function testUpdate()
+    {
+        $this->productIdLocator->expects($this->atLeastOnce())
+            ->method('retrieveProductIdsBySkus')
+            ->willReturn(['bundle' => ['2' => 'bundle']]);
+        $this->tierPriceValidator->expects($this->atLeastOnce())->method('validatePrices')->willReturn(true);
+        $this->tierPriceFactory->expects($this->atLeastOnce())->method('createSkeleton')->willReturn(
+            [
+                'row_id' => 2,
+                'all_groups' => 1,
+                'customer_group_id' => 0,
+                'qty' => 2,
+                'value' => 3,
+                'percentage_value' => null,
+                'website_id' => 0
+            ]
+        );
+        $this->tierPricePersistence->expects($this->once())
+            ->method('get')
+            ->willReturn(
+                [
+                    [
+                        'value_id' => 1,
+                        'row_id' => 2,
+                        'all_groups' => 1,
+                        'customer_group_id' => 0,
+                        'qty' => 2.0000,
+                        'value' => 2.0000,
+                        'percentage_value' => null,
+                        'website_id' => 0
+                    ]
+                ]
+            );
+        $this->tierPricePersistence->expects($this->atLeastOnce())->method('update');
+        $this->priceIndexer->expects($this->atLeastOnce())->method('execute');
+        $this->config->expects($this->atLeastOnce())->method('isEnabled')->willReturn(true);
+        $this->typeList->expects($this->atLeastOnce())->method('invalidate');
+        $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\TierPriceInterface::class)->getMockForAbstractClass();
+        $price->method('getSku')->willReturn('bundle');
+        $this->assertTrue($this->tierPriceStorage->update([$price]));
+    }
+
+    /**
+     * Test replace method.
+     * @return void
+     */
+    public function testReplace()
+    {
+        $this->tierPriceValidator->expects($this->atLeastOnce())->method('validatePrices');
+        $this->productIdLocator->expects($this->atLeastOnce())
+            ->method('retrieveProductIdsBySkus')
+            ->willReturn(['virtual' => ['2' => 'virtual']]);
+        $this->tierPriceFactory->expects($this->atLeastOnce())->method('createSkeleton')->willReturn(
+            [
+                'row_id' => 3,
+                'all_groups' => 1,
+                'customer_group_id' => 0,
+                'qty' => 3,
+                'value' => 7,
+                'percentage_value' => null,
+                'website_id' => 0
+            ]
+        );
+        $this->tierPricePersistence->expects($this->atLeastOnce())->method('replace');
+        $this->priceIndexer->expects($this->atLeastOnce())->method('execute');
+        $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\TierPriceInterface::class)->getMockForAbstractClass();
+        $price->method('getSku')->willReturn('virtual');
+        $this->config->expects($this->atLeastOnce())->method('isEnabled')->willReturn(true);
+        $this->typeList->expects($this->atLeastOnce())->method('invalidate');
+        $this->assertTrue($this->tierPriceStorage->replace([$price]));
+    }
+
+    /**
+     * Test delete method.
+     * @return void
+     */
+    public function testDelete()
+    {
+        $this->tierPriceValidator->expects($this->atLeastOnce())->method('validatePrices');
+        $this->productIdLocator->expects($this->atLeastOnce())
+            ->method('retrieveProductIdsBySkus')
+            ->willReturn(['simple' => ['2' => 'simple']]);
+        $this->tierPricePersistence->expects($this->once())
+            ->method('get')
+            ->willReturn(
+                [
+                    [
+                        'value_id' => 7,
+                        'row_id' => 7,
+                        'all_groups' => 1,
+                        'customer_group_id' => 0,
+                        'qty' => 5.0000,
+                        'value' => 6.0000,
+                        'percentage_value' => null,
+                        'website_id' => 0
+                    ]
+                ]
+            );
+        $this->tierPriceFactory->expects($this->atLeastOnce())->method('createSkeleton')->willReturn(
+            [
+                'row_id' => 3,
+                'all_groups' => 1,
+                'customer_group_id' => 0,
+                'qty' => 3,
+                'value' => 7,
+                'percentage_value' => null,
+                'website_id' => 0
+            ]
+        );
+        $this->tierPricePersistence->expects($this->atLeastOnce())->method('delete');
+        $this->priceIndexer->expects($this->atLeastOnce())->method('execute');
+        $this->config->expects($this->atLeastOnce())->method('isEnabled')->willReturn(true);
+        $this->typeList->expects($this->atLeastOnce())->method('invalidate');
+        $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\TierPriceInterface::class)->getMockForAbstractClass();
+        $price->method('getSku')->willReturn('simple');
+        $this->assertTrue($this->tierPriceStorage->delete([$price]));
+    }
+}
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceValidatorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceValidatorTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..1f44c2a75d1862ded128a2c35622dc148e3403a2
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceValidatorTest.php
@@ -0,0 +1,471 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Test\Unit\Model\Product\Price;
+
+use Magento\Catalog\Api\Data\TierPriceInterface;
+
+/**
+ * Class TierPriceValidatorTest.
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
+class TierPriceValidatorTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\Catalog\Model\ProductIdLocatorInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $productIdLocator;
+
+    /**
+     * @var \Magento\Framework\Api\SearchCriteriaBuilder|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $searchCriteriaBuilder;
+
+    /**
+     * @var \Magento\Framework\Api\FilterBuilder|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $filterBuilder;
+
+    /**
+     * @var \Magento\Customer\Api\GroupRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $customerGroupRepository;
+
+    /**
+     * @var \Magento\Store\Api\WebsiteRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $websiteRepository;
+
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\TierPricePersistence|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $tierPricePersistence;
+
+    /**
+     * @var \Magento\Catalog\Api\Data\TierPriceInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $tierPriceInterface;
+
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\TierPriceValidator
+     */
+    private $model;
+
+    /**
+     * Set up.
+     *
+     * @return void
+     */
+    protected function setUp()
+    {
+        $this->productIdLocator = $this->getMockForAbstractClass(
+            \Magento\Catalog\Model\ProductIdLocatorInterface::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            ['retrieveProductIdsBySkus']
+        );
+        $this->searchCriteriaBuilder = $this->getMock(
+            \Magento\Framework\Api\SearchCriteriaBuilder::class,
+            ['addFilters', 'create'],
+            [],
+            '',
+            false
+        );
+        $this->filterBuilder = $this->getMock(
+            \Magento\Framework\Api\FilterBuilder::class,
+            ['setField', 'setValue', 'create'],
+            [],
+            '',
+            false
+        );
+        $this->customerGroupRepository = $this->getMockForAbstractClass(
+            \Magento\Customer\Api\GroupRepositoryInterface::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            ['getList']
+        );
+        $this->websiteRepository = $this->getMockForAbstractClass(
+            \Magento\Store\Api\WebsiteRepositoryInterface::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            ['getById']
+        );
+        $this->tierPricePersistence = $this->getMock(
+            \Magento\Catalog\Model\Product\Price\TierPricePersistence::class,
+            ['addFilters', 'create'],
+            [],
+            '',
+            false
+        );
+        $this->tierPriceInterface = $this->getMockForAbstractClass(
+            \Magento\Catalog\Api\Data\TierPriceInterface::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            ['getSku', 'getPrice', 'getPriceType', 'getQuantity', 'getWebsiteId', 'getCustomerGroup']
+        );
+
+        $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+        $this->model = $objectManager->getObject(
+            \Magento\Catalog\Model\Product\Price\TierPriceValidator::class,
+            [
+                'productIdLocator' => $this->productIdLocator,
+                'searchCriteriaBuilder' => $this->searchCriteriaBuilder,
+                'filterBuilder' => $this->filterBuilder,
+                'customerGroupRepository' => $this->customerGroupRepository,
+                'websiteRepository' => $this->websiteRepository,
+                'tierPricePersistence' => $this->tierPricePersistence,
+                'allowedProductTypes' => ['simple', 'virtual', 'bundle', 'downloadable'],
+            ]
+        );
+    }
+
+    /**
+     * Test validateSkus method.
+     *
+     * @return void
+     */
+    public function testValidateSkus()
+    {
+        $skus = ['sku_1', 'sku_2'];
+        $idsBySku = [
+            'sku_1' => [1 => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE],
+            'sku_2' => [2 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL],
+        ];
+        $this->productIdLocator
+            ->expects($this->once())
+            ->method('retrieveProductIdsBySkus')
+            ->with($skus)
+            ->willReturn($idsBySku);
+        $this->model->validateSkus($skus);
+    }
+
+    /**
+     * Test validateSkus method throws exception.
+     *
+     * @expectedException \Magento\Framework\Exception\NoSuchEntityException
+     * @expectedExceptionMessage Requested products don't exist: sku_1, sku_2
+     */
+    public function testValidateSkusWithException()
+    {
+        $skus = ['sku_1', 'sku_2'];
+        $idsBySku = [
+            'sku_1' => [1 => 'grouped'],
+            'sku_2' => [2 => 'configurable'],
+        ];
+        $this->productIdLocator
+            ->expects($this->once())
+            ->method('retrieveProductIdsBySkus')
+            ->with($skus)
+            ->willReturn($idsBySku);
+        $this->model->validateSkus($skus);
+    }
+
+    /**
+     * Test validatePrices method.
+     *
+     * @return void
+     */
+    public function testValidatePrices()
+    {
+        $sku = 'sku_1';
+        $idsBySku = [
+            'sku_1' => [1 => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE],
+            'sku_2' => [2 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL],
+        ];
+        $productPrice = 15;
+        $this->tierPriceInterface->expects($this->exactly(8))->method('getSku')->willReturn($sku);
+        $this->productIdLocator->expects($this->exactly(2))->method('retrieveProductIdsBySkus')->willReturn($idsBySku);
+        $this->tierPriceInterface->expects($this->exactly(2))->method('getPrice')->willReturn($productPrice);
+        $this->tierPriceInterface
+            ->expects($this->exactly(2))
+            ->method('getPriceType')
+            ->willReturn(TierPriceInterface::PRICE_TYPE_FIXED);
+        $this->tierPriceInterface->expects($this->exactly(3))->method('getQuantity')->willReturn(2);
+        $this->checkWebsite($this->tierPriceInterface);
+        $this->checkGroup($this->tierPriceInterface);
+        $this->model->validatePrices([$this->tierPriceInterface], []);
+    }
+
+    /**
+     * Test validatePrices method with downloadable product.
+     *
+     * @expectedException \Magento\Framework\Exception\LocalizedException
+     * @expectedExceptionMessage Invalid attribute sku: .
+     */
+    public function testValidatePricesWithDownloadableProduct()
+    {
+        $this->tierPriceInterface->expects($this->exactly(2))->method('getSku')->willReturn(null);
+        $this->model->validatePrices([$this->tierPriceInterface], []);
+    }
+
+    /**
+     * Test validatePrices method with negative price.
+     *
+     * @expectedException \Magento\Framework\Exception\LocalizedException
+     * @expectedExceptionMessage Invalid attribute Price: -15.
+     */
+    public function testValidatePricesWithNegativePrice()
+    {
+        $negativePrice = -15;
+        $sku = 'sku_1';
+        $idsBySku = [
+            'sku_1' => [1 => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE],
+            'sku_2' => [2 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL],
+        ];
+        $this->tierPriceInterface->expects($this->exactly(3))->method('getSku')->willReturn($sku);
+        $this->productIdLocator->expects($this->exactly(2))->method('retrieveProductIdsBySkus')->willReturn($idsBySku);
+        $this->tierPriceInterface->expects($this->exactly(3))->method('getPrice')->willReturn($negativePrice);
+        $this->model->validatePrices([$this->tierPriceInterface], []);
+    }
+
+    /**
+     * Test validatePrices method with bundle product and fixed price.
+     *
+     * @expectedException \Magento\Framework\Exception\LocalizedException
+     * @expectedExceptionMessage Invalid attribute Price Type: fixed.
+     */
+    public function testValidatePricesWithBundleProductAndFixedPrice()
+    {
+        $sku = 'sku_1';
+        $idsBySku = [
+            'sku_1' => [1 => \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE],
+        ];
+        $productPrice = 15;
+        $this->tierPriceInterface->expects($this->exactly(4))->method('getSku')->willReturn($sku);
+        $this->productIdLocator->expects($this->exactly(2))->method('retrieveProductIdsBySkus')->willReturn($idsBySku);
+        $this->tierPriceInterface->expects($this->exactly(2))->method('getPrice')->willReturn($productPrice);
+        $this->tierPriceInterface
+            ->expects($this->exactly(4))
+            ->method('getPriceType')
+            ->willReturn(TierPriceInterface::PRICE_TYPE_FIXED);
+        $this->model->validatePrices([$this->tierPriceInterface], []);
+    }
+
+    /**
+     * Test validatePrices method with zero quantity.
+     *
+     * @expectedException \Magento\Framework\Exception\LocalizedException
+     * @expectedExceptionMessage Invalid attribute Quantity: 0.
+     */
+    public function testValidatePricesWithZeroQty()
+    {
+        $sku = 'sku_1';
+        $idsBySku = [
+            'sku_1' => [1 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL],
+        ];
+        $productPrice = 15;
+        $this->tierPriceInterface->expects($this->exactly(4))->method('getSku')->willReturn($sku);
+        $this->productIdLocator->expects($this->exactly(2))->method('retrieveProductIdsBySkus')->willReturn($idsBySku);
+        $this->tierPriceInterface->expects($this->exactly(2))->method('getPrice')->willReturn($productPrice);
+        $this->tierPriceInterface
+            ->expects($this->exactly(2))
+            ->method('getPriceType')
+            ->willReturn(TierPriceInterface::PRICE_TYPE_FIXED);
+        $this->tierPriceInterface->expects($this->exactly(2))->method('getQuantity')->willReturn(0);
+        $this->model->validatePrices([$this->tierPriceInterface], []);
+    }
+
+    /**
+     * Test validatePrices method without website.
+     *
+     * @expectedException \Magento\Framework\Exception\LocalizedException
+     * @expectedExceptionMessage Invalid attribute website_id: 15.
+     */
+    public function testValidatePricesWithoutWebsite()
+    {
+        $sku = 'sku_1';
+        $idsBySku = [
+            'sku_1' => [1 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL],
+        ];
+        $productPrice = 15;
+        $exception = new \Magento\Framework\Exception\NoSuchEntityException();
+        $this->tierPriceInterface->expects($this->exactly(4))->method('getSku')->willReturn($sku);
+        $this->productIdLocator->expects($this->exactly(2))->method('retrieveProductIdsBySkus')->willReturn($idsBySku);
+        $this->tierPriceInterface->expects($this->exactly(2))->method('getPrice')->willReturn($productPrice);
+        $this->tierPriceInterface
+            ->expects($this->exactly(2))
+            ->method('getPriceType')
+            ->willReturn(TierPriceInterface::PRICE_TYPE_FIXED);
+        $this->tierPriceInterface->expects($this->once())->method('getQuantity')->willReturn(2);
+        $this->websiteRepository->expects($this->once())->method('getById')->willThrowException($exception);
+        $this->tierPriceInterface->expects($this->exactly(2))->method('getWebsiteId')->willReturn(15);
+        $this->model->validatePrices([$this->tierPriceInterface], []);
+    }
+
+    /**
+     * Test validatePrices method not unique.
+     *
+     * @expectedException \Magento\Framework\Exception\LocalizedException
+     * @expectedExceptionMessage We found a duplicate website, tier price, customer
+     * group and quantity: Customer Group = retailer, Website Id = 2, Quantity = 2.
+     */
+    public function testValidatePricesNotUnique()
+    {
+        $sku = 'sku_1';
+        $idsBySku = [
+            'sku_1' => [1 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL],
+        ];
+        $productPrice = 15;
+        $this->tierPriceInterface->expects($this->exactly(8))->method('getSku')->willReturn($sku);
+        $this->productIdLocator->expects($this->exactly(2))->method('retrieveProductIdsBySkus')->willReturn($idsBySku);
+        $this->tierPriceInterface->expects($this->exactly(2))->method('getPrice')->willReturn($productPrice);
+        $this->tierPriceInterface
+            ->expects($this->exactly(2))
+            ->method('getPriceType')
+            ->willReturn(TierPriceInterface::PRICE_TYPE_FIXED);
+        $website = $this->getMockForAbstractClass(
+            \Magento\Store\Api\Data\WebsiteInterface::class,
+            [],
+            '',
+            false
+        );
+        $this->tierPriceInterface
+            ->expects($this->exactly(5))
+            ->method('getWebsiteId')
+            ->willReturnOnConsecutiveCalls(1, 0, 0, 1, 2);
+        $this->websiteRepository->expects($this->once())->method('getById')->willReturn($website);
+        $this->tierPriceInterface->expects($this->exactly(4))->method('getQuantity')->willReturn(2);
+        $this->tierPriceInterface->expects($this->exactly(3))->method('getCustomerGroup')->willReturn('retailer');
+        $this->model->validatePrices([$this->tierPriceInterface], []);
+    }
+
+    /**
+     * Test validatePrices method without group.
+     *
+     * @expectedException \Magento\Framework\Exception\LocalizedException
+     * @expectedExceptionMessage No such entity with Customer Group = wholesale.
+     */
+    public function testValidatePricesWithoutGroup()
+    {
+        $sku = 'sku_1';
+        $idsBySku = [
+            'sku_1' => [1 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL],
+        ];
+        $productPrice = 15;
+        $this->tierPriceInterface->expects($this->exactly(8))->method('getSku')->willReturn($sku);
+        $this->productIdLocator->expects($this->exactly(2))->method('retrieveProductIdsBySkus')->willReturn($idsBySku);
+        $this->tierPriceInterface->expects($this->exactly(2))->method('getPrice')->willReturn($productPrice);
+        $this->tierPriceInterface
+            ->expects($this->exactly(2))
+            ->method('getPriceType')
+            ->willReturn(TierPriceInterface::PRICE_TYPE_FIXED);
+        $this->tierPriceInterface->expects($this->exactly(3))->method('getQuantity')->willReturn(2);
+        $this->checkWebsite($this->tierPriceInterface);
+        $searchCriteria = $this->getMock(
+            \Magento\Framework\Api\SearchCriteria::class,
+            [],
+            [],
+            '',
+            false
+        );
+        $searchResults = $this->getMockForAbstractClass(
+            \Magento\Customer\Api\Data\GroupSearchResultsInterface::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            ['getItems']
+        );
+        $this->tierPriceInterface->expects($this->exactly(3))->method('getCustomerGroup')->willReturn('wholesale');
+        $this->searchCriteriaBuilder->expects($this->once())->method('addFilters')->willReturnSelf();
+        $this->filterBuilder->expects($this->once())->method('setField')->with('customer_group_code')->willReturnSelf();
+        $this->filterBuilder->expects($this->once())->method('setValue')->with('wholesale')->willReturnSelf();
+        $this->filterBuilder->expects($this->once())->method('create')->willReturnSelf();
+        $this->searchCriteriaBuilder
+            ->expects($this->once())
+            ->method('create')
+            ->willReturn($searchCriteria);
+        $this->customerGroupRepository
+            ->expects($this->once())
+            ->method('getList')
+            ->with($searchCriteria)
+            ->willReturn($searchResults);
+        $searchResults->expects($this->once())->method('getItems')->willReturn([]);
+        $this->model->validatePrices([$this->tierPriceInterface], []);
+    }
+
+    /**
+     * Check website.
+     *
+     * @param \PHPUnit_Framework_MockObject_MockObject $price
+     */
+    private function checkWebsite(\PHPUnit_Framework_MockObject_MockObject $price)
+    {
+        $website = $this->getMockForAbstractClass(
+            \Magento\Store\Api\Data\WebsiteInterface::class,
+            [],
+            '',
+            false
+        );
+        $price->expects($this->exactly(3))->method('getWebsiteId')->willReturn(1);
+        $this->websiteRepository->expects($this->once())->method('getById')->willReturn($website);
+    }
+
+    /**
+     * Check group.
+     *
+     * @param \PHPUnit_Framework_MockObject_MockObject $price
+     */
+    private function checkGroup(\PHPUnit_Framework_MockObject_MockObject $price)
+    {
+        $searchCriteria = $this->getMock(
+            \Magento\Framework\Api\SearchCriteria::class,
+            [],
+            [],
+            '',
+            false
+        );
+        $searchResults = $this->getMockForAbstractClass(
+            \Magento\Customer\Api\Data\GroupSearchResultsInterface::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            ['getItems']
+        );
+        $group = $this->getMockForAbstractClass(
+            \Magento\Customer\Api\Data\GroupInterface::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            ['getCode', 'getId']
+        );
+
+        $price->expects($this->exactly(3))->method('getCustomerGroup')->willReturn('wholesale');
+        $this->searchCriteriaBuilder->expects($this->once())->method('addFilters')->willReturnSelf();
+        $this->filterBuilder->expects($this->once())->method('setField')->with('customer_group_code')->willReturnSelf();
+        $this->filterBuilder->expects($this->once())->method('setValue')->with('wholesale')->willReturnSelf();
+        $this->filterBuilder->expects($this->once())->method('create')->willReturnSelf();
+        $this->searchCriteriaBuilder
+            ->expects($this->once())
+            ->method('create')
+            ->willReturn($searchCriteria);
+        $this->customerGroupRepository
+            ->expects($this->once())
+            ->method('getList')
+            ->with($searchCriteria)
+            ->willReturn($searchResults);
+        $searchResults->expects($this->once())->method('getItems')->willReturn([$group]);
+        $group->expects($this->once())->method('getCode')->willReturn('wholesale');
+        $group->expects($this->once())->method('getId')->willReturn(4);
+    }
+}
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductIdLocatorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductIdLocatorTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..75f81195f02e420929edbea8580692c1125752b4
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductIdLocatorTest.php
@@ -0,0 +1,158 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Test\Unit\Model;
+
+/**
+ * Class ProductIdLocatorTest.
+ */
+class ProductIdLocatorTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\Framework\Api\SearchCriteriaBuilder|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $searchCriteriaBuilder;
+
+    /**
+     * @var \Magento\Framework\Api\FilterBuilder|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $filterBuilder;
+
+    /**
+     * @var \Magento\Framework\EntityManager\MetadataPool|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $metadataPool;
+
+    /**
+     * @var \Magento\Catalog\Api\ProductRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $productRepository;
+
+    /**
+     * @var \Magento\Catalog\Model\ProductIdLocator
+     */
+    private $model;
+
+    /**
+     * Set up.
+     *
+     * @return void
+     */
+    protected function setUp()
+    {
+        $this->searchCriteriaBuilder = $this->getMock(
+            \Magento\Framework\Api\SearchCriteriaBuilder::class,
+            ['addFilters', 'create'],
+            [],
+            '',
+            false
+        );
+        $this->filterBuilder = $this->getMock(
+            \Magento\Framework\Api\FilterBuilder::class,
+            ['setField', 'setConditionType', 'setValue', 'create'],
+            [],
+            '',
+            false
+        );
+        $this->metadataPool = $this->getMock(
+            \Magento\Framework\EntityManager\MetadataPool::class,
+            ['getMetadata'],
+            [],
+            '',
+            false
+        );
+        $this->productRepository = $this->getMockForAbstractClass(
+            \Magento\Catalog\Api\ProductRepositoryInterface::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            ['getList']
+        );
+
+        $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+        $this->model = $objectManager->getObject(
+            \Magento\Catalog\Model\ProductIdLocator::class,
+            [
+                'searchCriteriaBuilder' => $this->searchCriteriaBuilder,
+                'filterBuilder' => $this->filterBuilder,
+                'metadataPool' => $this->metadataPool,
+                'productRepository' => $this->productRepository,
+            ]
+        );
+    }
+
+    /**
+     * Test retrieve
+     */
+    public function testRetrieveProductIdsBySkus()
+    {
+        $skus = ['sku_1', 'sku_2'];
+        $searchCriteria = $this->getMock(
+            \Magento\Framework\Api\SearchCriteria::class,
+            [],
+            [],
+            '',
+            false
+        );
+        $searchResults = $this->getMockForAbstractClass(
+            \Magento\Catalog\Api\Data\ProductSearchResultsInterface::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            ['getItems']
+        );
+        $product = $this->getMockForAbstractClass(
+            \Magento\Catalog\Api\Data\ProductInterface::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            ['getSku', 'getData', 'getTypeId']
+        );
+        $metaDataInterface = $this->getMockForAbstractClass(
+            \Magento\Framework\EntityManager\EntityMetadataInterface::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            ['getLinkField']
+        );
+        $this->searchCriteriaBuilder->expects($this->once())->method('addFilters')->willReturnSelf();
+        $this->filterBuilder->expects($this->once())->method('setField')->with('sku')->willReturnSelf();
+        $this->filterBuilder->expects($this->once())->method('setConditionType')->with('in')->willReturnSelf();
+        $this->filterBuilder->expects($this->once())->method('setValue')->with(['sku_1', 'sku_2'])->willReturnSelf();
+        $this->filterBuilder->expects($this->once())->method('create')->willReturnSelf();
+        $this->searchCriteriaBuilder
+            ->expects($this->once())
+            ->method('create')
+            ->willReturn($searchCriteria);
+        $this->productRepository
+            ->expects($this->once())
+            ->method('getList')
+            ->with($searchCriteria)
+            ->willReturn($searchResults);
+        $searchResults->expects($this->once())->method('getItems')->willReturn([$product]);
+        $this->metadataPool
+            ->expects($this->once())
+            ->method('getMetadata')
+            ->with(\Magento\Catalog\Api\Data\ProductInterface::class)
+            ->willReturn($metaDataInterface);
+        $metaDataInterface->expects($this->once())->method('getLinkField')->willReturn('entity_id');
+        $product->expects($this->once())->method('getSku')->willReturn('sku_1');
+        $product->expects($this->once())->method('getData')->with('entity_id')->willReturn(1);
+        $product->expects($this->once())->method('getTypeId')->willReturn('simple');
+        $this->assertEquals(
+            ['sku_1' => [1 => 'simple']],
+            $this->model->retrieveProductIdsBySkus($skus)
+        );
+    }
+}
diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php
index 53360de08b434b4a0314fe341760c55fef476ef4..326e92417670a77bc269625153f3a56f715bb7e5 100644
--- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php
+++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/AdvancedPricing.php
@@ -407,6 +407,7 @@ class AdvancedPricing extends AbstractModifier
                 'data' => [
                     'config' => [
                         'componentType' => 'dynamicRows',
+                        'component' => 'Magento_Catalog/js/components/dynamic-rows-tier-price',
                         'label' => __('Customer Group Price'),
                         'renderDefaultRecord' => false,
                         'recordTemplate' => 'record',
diff --git a/app/code/Magento/Catalog/etc/adminhtml/di.xml b/app/code/Magento/Catalog/etc/adminhtml/di.xml
index 5fc5ec8d44fd0a88f8a5d542d5518fa60ad77629..d6ecaa7c40391de2b12abac33d25b9ecd5620af8 100644
--- a/app/code/Magento/Catalog/etc/adminhtml/di.xml
+++ b/app/code/Magento/Catalog/etc/adminhtml/di.xml
@@ -169,4 +169,9 @@
             <argument name="scopeName" xsi:type="string">product_form.product_form</argument>
         </arguments>
     </type>
+    <type name="Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\TierPrice">
+        <arguments>
+            <argument name="productPriceOptions" xsi:type="object">Magento\Catalog\Model\Config\Source\Product\Options\TierPrice</argument>
+        </arguments>
+    </type>
 </config>
diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml
index 27b9a19065e99f22fccf4f2f42076c69d0f1e0c2..0142f7c2f261837ae1e0d8f25418486951da3043 100644
--- a/app/code/Magento/Catalog/etc/di.xml
+++ b/app/code/Magento/Catalog/etc/di.xml
@@ -51,6 +51,13 @@
     <preference for="Magento\Catalog\Model\Product\Pricing\Renderer\SalableResolverInterface" type="Magento\Catalog\Model\Product\Pricing\Renderer\SalableResolver"/>
     <preference for="Magento\Catalog\Model\Product\Media\ConfigInterface" type="Magento\Catalog\Model\Product\Media\Config"/>
     <preference for="Magento\Framework\View\Asset\ContextInterface" type="Magento\Catalog\Model\View\Asset\Image\Context"/>
+    <preference for="Magento\Catalog\Api\TierPriceStorageInterface" type="Magento\Catalog\Model\Product\Price\TierPriceStorage" />
+    <preference for="Magento\Catalog\Api\Data\TierPriceInterface" type="Magento\Catalog\Model\Product\Price\TierPrice" />
+    <preference for="Magento\Catalog\Api\BasePriceStorageInterface" type="Magento\Catalog\Model\Product\Price\BasePriceStorage" />
+    <preference for="Magento\Catalog\Api\Data\BasePriceInterface" type="Magento\Catalog\Model\Product\Price\BasePrice" />
+    <preference for="Magento\Catalog\Api\CostStorageInterface" type="Magento\Catalog\Model\Product\Price\CostStorage" />
+    <preference for="Magento\Catalog\Api\Data\CostInterface" type="Magento\Catalog\Model\Product\Price\Cost" />
+    <preference for="Magento\Catalog\Model\ProductIdLocatorInterface" type="Magento\Catalog\Model\ProductIdLocator" />
     <type name="Magento\Customer\Model\ResourceModel\Visitor">
         <plugin name="catalogLog" type="Magento\Catalog\Model\Plugin\Log" />
     </type>
@@ -848,4 +855,30 @@
             </argument>
         </arguments>
     </type>
+    <type name="Magento\Catalog\Model\Product\Price\CostStorage">
+        <arguments>
+            <argument name="allowedProductTypes" xsi:type="array">
+                <item name="0" xsi:type="string">simple</item>
+                <item name="1" xsi:type="string">virtual</item>
+            </argument>
+        </arguments>
+    </type>
+    <type name="Magento\Catalog\Model\Product\Price\BasePriceStorage">
+        <arguments>
+            <argument name="allowedProductTypes" xsi:type="array">
+                <item name="0" xsi:type="string">simple</item>
+                <item name="1" xsi:type="string">virtual</item>
+                <item name="2" xsi:type="string">bundle</item>
+            </argument>
+        </arguments>
+    </type>
+    <type name="Magento\Catalog\Model\Product\Price\TierPriceValidator">
+        <arguments>
+            <argument name="allowedProductTypes" xsi:type="array">
+                <item name="0" xsi:type="string">simple</item>
+                <item name="1" xsi:type="string">virtual</item>
+                <item name="2" xsi:type="string">bundle</item>
+            </argument>
+        </arguments>
+    </type>
 </config>
diff --git a/app/code/Magento/Catalog/etc/webapi.xml b/app/code/Magento/Catalog/etc/webapi.xml
index 99670c347a89babc45714d5ed0350676f5cf48fe..b53f11b1ff2959a583b1a401a7566ca7eee33589 100644
--- a/app/code/Magento/Catalog/etc/webapi.xml
+++ b/app/code/Magento/Catalog/etc/webapi.xml
@@ -246,6 +246,60 @@
             <resource ref="Magento_Catalog::catalog"/>
         </resources>
     </route>
+    <route url="/V1/products/tier-prices-information" method="POST">
+        <service class="Magento\Catalog\Api\TierPriceStorageInterface" method="get"/>
+        <resources>
+            <resource ref="Magento_Catalog::catalog"/>
+        </resources>
+    </route>
+    <route url="/V1/products/tier-prices" method="POST">
+        <service class="Magento\Catalog\Api\TierPriceStorageInterface" method="update"/>
+        <resources>
+            <resource ref="Magento_Catalog::catalog"/>
+        </resources>
+    </route>
+    <route url="/V1/products/tier-prices" method="PUT">
+        <service class="Magento\Catalog\Api\TierPriceStorageInterface" method="replace"/>
+        <resources>
+            <resource ref="Magento_Catalog::catalog"/>
+        </resources>
+    </route>
+    <route url="/V1/products/tier-prices-delete" method="POST">
+        <service class="Magento\Catalog\Api\TierPriceStorageInterface" method="delete"/>
+        <resources>
+            <resource ref="Magento_Catalog::catalog"/>
+        </resources>
+    </route>
+    <route url="/V1/products/base-prices-information" method="POST">
+        <service class="Magento\Catalog\Api\BasePriceStorageInterface" method="get"/>
+        <resources>
+            <resource ref="Magento_Catalog::catalog"/>
+        </resources>
+    </route>
+    <route url="/V1/products/base-prices" method="POST">
+        <service class="Magento\Catalog\Api\BasePriceStorageInterface" method="update"/>
+        <resources>
+            <resource ref="Magento_Catalog::catalog"/>
+        </resources>
+    </route>
+    <route url="/V1/products/cost-information" method="POST">
+        <service class="Magento\Catalog\Api\CostStorageInterface" method="get"/>
+        <resources>
+            <resource ref="Magento_Catalog::catalog"/>
+        </resources>
+    </route>
+    <route url="/V1/products/cost" method="POST">
+        <service class="Magento\Catalog\Api\CostStorageInterface" method="update"/>
+        <resources>
+            <resource ref="Magento_Catalog::catalog"/>
+        </resources>
+    </route>
+    <route url="/V1/products/cost-delete" method="POST">
+        <service class="Magento\Catalog\Api\CostStorageInterface" method="delete"/>
+        <resources>
+            <resource ref="Magento_Catalog::catalog"/>
+        </resources>
+    </route>
 
     <route url="/V1/categories/:categoryId" method="DELETE">
         <service class="Magento\Catalog\Api\CategoryRepositoryInterface" method="deleteByIdentifier" />
diff --git a/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js b/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js
new file mode 100644
index 0000000000000000000000000000000000000000..6dc4c747a44514be814ea4d939135070287052d8
--- /dev/null
+++ b/app/code/Magento/Catalog/view/adminhtml/web/js/components/dynamic-rows-tier-price.js
@@ -0,0 +1,29 @@
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+define([
+    'underscore',
+    'Magento_Ui/js/dynamic-rows/dynamic-rows'
+], function (_, DynamicRows) {
+    'use strict';
+
+    return DynamicRows.extend({
+
+        /**
+         * Init header elements
+         */
+        initHeader: function () {
+            var labels;
+
+            this._super();
+            labels = _.clone(this.labels());
+            labels = _.sortBy(labels, function (label) {
+                return label.sortOrder;
+            });
+
+            this.labels(labels);
+        }
+    });
+});
diff --git a/app/code/Magento/Customer/Setup/UpgradeSchema.php b/app/code/Magento/Customer/Setup/UpgradeSchema.php
index 33ec2352e865d0b6fd5cbbde5d9646b5dd92975d..46ccd83fcaae11b3ab4720e5ead60b3625bbda38 100755
--- a/app/code/Magento/Customer/Setup/UpgradeSchema.php
+++ b/app/code/Magento/Customer/Setup/UpgradeSchema.php
@@ -136,14 +136,12 @@ class UpgradeSchema implements UpgradeSchemaInterface
             ]
         );
         foreach ($keys as $key) {
-            $setup->getConnection()->modifyColumn(
+            $description = $setup->getConnection()->describeTable($key['TABLE_NAME'])[$key['COLUMN_NAME']];
+            $description['DATA_TYPE'] = 'int';
+            $setup->getConnection()->modifyColumnByDdl(
                 $key['TABLE_NAME'],
                 $key['COLUMN_NAME'],
-                [
-                    'type' => 'integer',
-                    'unsigned' => true,
-                    'nullable' => false
-                ]
+                $description
             );
         }
     }
diff --git a/app/code/Magento/Downloadable/etc/di.xml b/app/code/Magento/Downloadable/etc/di.xml
index d7e2aafe79582d0f50a419cdc9bcf7f2f090cdcd..8e3d0bb6c5c5ae598a8365770f7e2ae932c78d73 100644
--- a/app/code/Magento/Downloadable/etc/di.xml
+++ b/app/code/Magento/Downloadable/etc/di.xml
@@ -123,4 +123,25 @@
             </argument>
         </arguments>
     </type>
+    <type name="Magento\Catalog\Model\Product\Price\CostStorage">
+        <arguments>
+            <argument name="allowedProductTypes" xsi:type="array">
+                <item name="2" xsi:type="string">downloadable</item>
+            </argument>
+        </arguments>
+    </type>
+    <type name="Magento\Catalog\Model\Product\Price\TierPriceValidator">
+        <arguments>
+            <argument name="allowedProductTypes" xsi:type="array">
+                <item name="3" xsi:type="string">downloadable</item>
+            </argument>
+        </arguments>
+    </type>
+    <type name="Magento\Catalog\Model\Product\Price\BasePriceStorage">
+        <arguments>
+            <argument name="allowedProductTypes" xsi:type="array">
+                <item name="3" xsi:type="string">downloadable</item>
+            </argument>
+        </arguments>
+    </type>
 </config>
diff --git a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js
index 449418f07c100c91e139231c704bb28fd8c9ae82..bfae1cf87030f45d8bbda74e8eb3d9f741f00201 100644
--- a/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js
+++ b/app/code/Magento/Ui/view/base/web/js/dynamic-rows/dynamic-rows.js
@@ -538,7 +538,8 @@ define([
                         label: cell.config.label,
                         name: cell.name,
                         required: !!cell.config.validation,
-                        columnsHeaderClasses: cell.config.columnsHeaderClasses
+                        columnsHeaderClasses: cell.config.columnsHeaderClasses,
+                        sortOrder: cell.config.sortOrder
                     });
 
                     this.labels.push(data);
diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/BasePriceStorageTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/BasePriceStorageTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..aad068ce6f0799feb342e93b3f94c6e6899c18c9
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/BasePriceStorageTest.php
@@ -0,0 +1,100 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Api;
+
+use Magento\TestFramework\TestCase\WebapiAbstract;
+
+/**
+ * BasePriceStorage test.
+ */
+class BasePriceStorageTest extends WebapiAbstract
+{
+    const SERVICE_NAME = 'catalogBasePriceStorageV1';
+    const SERVICE_VERSION = 'V1';
+    const SIMPLE_PRODUCT_SKU = 'simple';
+
+    /**
+     * @var \Magento\TestFramework\ObjectManager
+     */
+    private $objectManager;
+
+    /**
+     * Set up.
+     */
+    protected function setUp()
+    {
+        $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+    }
+
+    /**
+     * Test get method.
+     *
+     * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php
+     */
+    public function testGet()
+    {
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => '/V1/products/base-prices-information',
+                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST
+            ],
+            'soap' => [
+                'service' => self::SERVICE_NAME,
+                'serviceVersion' => self::SERVICE_VERSION,
+                'operation' => self::SERVICE_NAME . 'Get',
+            ],
+        ];
+        $response = $this->_webApiCall($serviceInfo, ['skus' => [self::SIMPLE_PRODUCT_SKU]]);
+        $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+        /** @var \Magento\Catalog\Api\Data\ProductInterface $product */
+        $product = $productRepository->get(self::SIMPLE_PRODUCT_SKU);
+
+        $this->assertNotEmpty($response);
+        $this->assertEquals($product->getPrice(), $response[0]['price']);
+        $this->assertEquals($product->getSku(), $response[0]['sku']);
+    }
+
+    /**
+     * Test update method.
+     *
+     * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php
+     */
+    public function testUpdate()
+    {
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => '/V1/products/base-prices',
+                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST
+            ],
+            'soap' => [
+                'service' => self::SERVICE_NAME,
+                'serviceVersion' => self::SERVICE_VERSION,
+                'operation' => self::SERVICE_NAME . 'Update',
+            ],
+        ];
+        $newPrice = 9999;
+        $storeId = 0;
+        $response = $this->_webApiCall(
+            $serviceInfo,
+            [
+                'prices' => [
+                    [
+                        'price' => $newPrice,
+                        'store_id' => $storeId,
+                        'sku' => self::SIMPLE_PRODUCT_SKU,
+                    ]
+                ]
+            ]
+        );
+        $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+        /** @var \Magento\Catalog\Api\Data\ProductInterface $product */
+        $product = $productRepository->get(self::SIMPLE_PRODUCT_SKU);
+
+        $this->assertNotEmpty($response);
+        $this->assertEquals($product->getPrice(), $newPrice);
+    }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CostStorageTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CostStorageTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..6af9960a524dfe63910b51dbee08335af109d909
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CostStorageTest.php
@@ -0,0 +1,129 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Api;
+
+use Magento\TestFramework\TestCase\WebapiAbstract;
+
+/**
+ * CostStorage test.
+ */
+class CostStorageTest extends WebapiAbstract
+{
+    const SERVICE_NAME = 'catalogCostStorageV1';
+    const SERVICE_VERSION = 'V1';
+    const SIMPLE_PRODUCT_SKU = 'simple';
+
+    /**
+     * @var \Magento\TestFramework\ObjectManager
+     */
+    private $objectManager;
+
+    /**
+     * Set up.
+     */
+    protected function setUp()
+    {
+        $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+    }
+
+    /**
+     * Test get method.
+     *
+     * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php
+     */
+    public function testGet()
+    {
+        $cost = 3057;
+        $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+        $productRepository->save($productRepository->get(self::SIMPLE_PRODUCT_SKU)->setData('cost', $cost));
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => '/V1/products/cost-information',
+                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST
+            ],
+            'soap' => [
+                'service' => self::SERVICE_NAME,
+                'serviceVersion' => self::SERVICE_VERSION,
+                'operation' => self::SERVICE_NAME . 'Delete',
+            ],
+        ];
+        $response = $this->_webApiCall($serviceInfo, ['skus' => [self::SIMPLE_PRODUCT_SKU]]);
+
+        /** @var \Magento\Catalog\Api\Data\ProductInterface $product */
+        $product = $productRepository->get(self::SIMPLE_PRODUCT_SKU);
+
+        $this->assertNotEmpty($response);
+        $this->assertEquals($product->getCost(), $cost);
+    }
+
+    /**
+     * Test update method.
+     *
+     * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php
+     */
+    public function testUpdate()
+    {
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => '/V1/products/cost',
+                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST
+            ],
+            'soap' => [
+                'service' => self::SERVICE_NAME,
+                'serviceVersion' => self::SERVICE_VERSION,
+                'operation' => self::SERVICE_NAME . 'Update',
+            ],
+        ];
+        $storeId = 0;
+        $newCost = 31337;
+        $response = $this->_webApiCall(
+            $serviceInfo,
+            [
+                'prices' => [
+                    [
+                        'cost' => $newCost,
+                        'store_id' => $storeId,
+                        'sku' => self::SIMPLE_PRODUCT_SKU,
+                    ]
+                ]
+            ]
+        );
+        $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+        /** @var \Magento\Catalog\Api\Data\ProductInterface $product */
+        $product = $productRepository->get(self::SIMPLE_PRODUCT_SKU);
+        $this->assertNotEmpty($response);
+        $this->assertEquals($product->getCost(), $newCost);
+    }
+
+    /**
+     * Test delete method.
+     *
+     * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php
+     */
+    public function testDelete()
+    {
+        $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+        $productRepository->save($productRepository->get(self::SIMPLE_PRODUCT_SKU)->setData('cost', 777));
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => '/V1/products/cost-delete',
+                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST
+            ],
+            'soap' => [
+                'service' => self::SERVICE_NAME,
+                'serviceVersion' => self::SERVICE_VERSION,
+                'operation' => self::SERVICE_NAME . 'Delete',
+            ],
+        ];
+        $response = $this->_webApiCall($serviceInfo, ['skus' => [self::SIMPLE_PRODUCT_SKU]]);
+        $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+        /** @var \Magento\Catalog\Api\Data\ProductInterface $product */
+        $product = $productRepository->get(self::SIMPLE_PRODUCT_SKU);
+        $this->assertTrue($response);
+        $this->assertNull($product->getCost());
+    }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/TierPriceStorageTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/TierPriceStorageTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..232c63257215036e414236fba818ff876da97c18
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/TierPriceStorageTest.php
@@ -0,0 +1,231 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Catalog\Api;
+
+use Magento\TestFramework\TestCase\WebapiAbstract;
+
+/**
+ * TierPriceStorage test.
+ */
+class TierPriceStorageTest extends WebapiAbstract
+{
+    const SERVICE_NAME = 'catalogTierPriceStorageV1';
+    const SERVICE_VERSION = 'V1';
+    const SIMPLE_PRODUCT_SKU = 'simple';
+
+    /**
+     * @var \Magento\TestFramework\ObjectManager
+     */
+    private $objectManager;
+
+    /**
+     * Set up.
+     */
+    protected function setUp()
+    {
+        $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+    }
+
+    /**
+     * Test get method.
+     *
+     * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php
+     */
+    public function testGet()
+    {
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => '/V1/products/tier-prices-information',
+                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST
+            ],
+            'soap' => [
+                'service' => self::SERVICE_NAME,
+                'serviceVersion' => self::SERVICE_VERSION,
+                'operation' => self::SERVICE_NAME . 'Get',
+            ],
+        ];
+        $response = $this->_webApiCall($serviceInfo, ['skus' => [self::SIMPLE_PRODUCT_SKU]]);
+        $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+        /** @var \Magento\Catalog\Api\Data\ProductInterface $product */
+        $tierPrices = $productRepository->get(self::SIMPLE_PRODUCT_SKU)->getTierPrices();
+        $this->assertNotEmpty($response);
+        $this->assertEquals(count($response), count($tierPrices));
+
+        foreach ($response as $item) {
+            $this->assertTrue($this->isPriceCorrect($item, $tierPrices));
+        }
+    }
+
+    /**
+     * Test update method.
+     *
+     * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php
+     */
+    public function testUpdate()
+    {
+        $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+        $prices = $productRepository->get(self::SIMPLE_PRODUCT_SKU)->getTierPrices();
+        $tierPrice = array_shift($prices);
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => '/V1/products/tier-prices',
+                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST
+            ],
+            'soap' => [
+                'service' => self::SERVICE_NAME,
+                'serviceVersion' => self::SERVICE_VERSION,
+                'operation' => self::SERVICE_NAME . 'Update',
+            ],
+        ];
+        $newPrice = [
+            'price' => 40,
+            'price_type' => \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_DISCOUNT,
+            'website_id' => 0,
+            'sku' => self::SIMPLE_PRODUCT_SKU,
+            'customer_group' => 'ALL GROUPS',
+            'quantity' => 7778
+        ];
+        $updatedPrice = [
+            'price' => 778,
+            'price_type' => \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_FIXED,
+            'website_id' => 0,
+            'sku' => self::SIMPLE_PRODUCT_SKU,
+            'customer_group' => 'ALL GROUPS',
+            'quantity' => $tierPrice->getQty()
+        ];
+        $response = $this->_webApiCall($serviceInfo, ['prices' => [$updatedPrice, $newPrice]]);
+        $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+        $tierPrices = $productRepository->get(self::SIMPLE_PRODUCT_SKU)->getTierPrices();
+        $this->assertTrue($response);
+        $this->assertTrue($this->isPriceCorrect($newPrice, $tierPrices));
+        $this->assertTrue($this->isPriceCorrect($updatedPrice, $tierPrices));
+    }
+
+    /**
+     * Test replace method.
+     *
+     * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php
+     */
+    public function testReplace()
+    {
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => '/V1/products/tier-prices',
+                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_PUT
+            ],
+            'soap' => [
+                'service' => self::SERVICE_NAME,
+                'serviceVersion' => self::SERVICE_VERSION,
+                'operation' => self::SERVICE_NAME . 'Replace',
+            ],
+        ];
+        $newPrices = [
+            [
+                'price' => 50,
+                'price_type' => \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_DISCOUNT,
+                'website_id' => 0,
+                'sku' => self::SIMPLE_PRODUCT_SKU,
+                'customer_group' => 'general',
+                'quantity' => 7778
+            ],
+            [
+                'price' => 70,
+                'price_type' => \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_FIXED,
+                'website_id' => 0,
+                'sku' => self::SIMPLE_PRODUCT_SKU,
+                'customer_group' => 'general',
+                'quantity' => 33
+            ]
+        ];
+        $response = $this->_webApiCall($serviceInfo, ['prices' => $newPrices]);
+        $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+        /** @var \Magento\Catalog\Api\Data\ProductInterface $product */
+        $tierPrices = $productRepository->get(self::SIMPLE_PRODUCT_SKU)->getTierPrices();
+        $this->assertTrue($response);
+        $this->assertEquals(count($newPrices), count($tierPrices));
+
+        foreach ($newPrices as $newPrice) {
+            $this->assertTrue($this->isPriceCorrect($newPrice, $tierPrices));
+        }
+    }
+
+    /**
+     * Test delete method.
+     *
+     * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php
+     */
+    public function testDelete()
+    {
+        $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+        $tierPrices = $productRepository->get(self::SIMPLE_PRODUCT_SKU)->getTierPrices();
+        $pricesToStore = array_pop($tierPrices);
+        $pricesToDelete = [];
+        foreach ($tierPrices as $tierPrice) {
+            $tierPriceValue = $tierPrice->getExtensionAttributes()->getPercentageValue()
+                ?: $tierPrice->getValue();
+            $priceType = $tierPrice->getExtensionAttributes()->getPercentageValue()
+                ? \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_DISCOUNT
+                : \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_FIXED;
+            $customerGroup = $tierPrice->getCustomerGroupId() == \Magento\Customer\Model\Group::NOT_LOGGED_IN_ID
+                ? 'NOT LOGGED IN'
+                : 'ALL GROUPS';
+            $pricesToDelete[] = [
+                'price' => $tierPriceValue,
+                'price_type' => $priceType,
+                'website_id' => 0,
+                'customer_group' => $customerGroup,
+                'sku' => self::SIMPLE_PRODUCT_SKU,
+                'quantity' => $tierPrice->getQty()
+
+            ];
+        }
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => '/V1/products/tier-prices-delete',
+                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST
+            ],
+            'soap' => [
+                'service' => self::SERVICE_NAME,
+                'serviceVersion' => self::SERVICE_VERSION,
+                'operation' => self::SERVICE_NAME . 'Delete',
+            ],
+        ];
+        $response = $this->_webApiCall($serviceInfo, ['prices' => $pricesToDelete]);
+        $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+        $tierPrices = $productRepository->get(self::SIMPLE_PRODUCT_SKU)->getTierPrices();
+        $tierPrice = $tierPrices[0];
+        $this->assertTrue($response);
+        $this->assertEquals(1, count($tierPrices));
+        $this->assertEquals($pricesToStore, $tierPrice);
+    }
+
+    /**
+     * Check prise exists and is correct.
+     *
+     * @param array $price
+     * @param array $tierPrices
+     * @return bool
+     */
+    private function isPriceCorrect(array $price, array $tierPrices)
+    {
+        $isCorrect = false;
+
+        foreach ($tierPrices as $tierPrice) {
+            $priceIsCorrect = $price['price_type'] === \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_DISCOUNT
+                ? (float)$tierPrice->getExtensionAttributes()->getPercentageValue() === (float)$price['price']
+                : (float)$tierPrice->getValue() === (float)$price['price'];
+            if (
+                $priceIsCorrect
+                && (int)$tierPrice->getQty() === (int)$price['quantity']
+                && $tierPrice->getExtensionAttributes()->getWebsiteId() == $price['website_id']
+            ) {
+                $isCorrect = true;
+            }
+        }
+
+        return $isCorrect;
+    }
+}