diff --git a/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePanel.php b/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePanel.php
index b0d218dc2e285627ec835808ba4e505fd3c593aa..95534bd114b49af653e56df164fdd7d21fcfc419 100644
--- a/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePanel.php
+++ b/app/code/Magento/Bundle/Ui/DataProvider/Product/Form/Modifier/BundlePanel.php
@@ -203,17 +203,19 @@ class BundlePanel extends AbstractModifier
         $pricePath = $this->arrayManager->slicePath($pricePath, 0, -1) . '/value_type/arguments/data/options';
 
         $price = $this->arrayManager->get($pricePath, $meta);
-        $meta = $this->arrayManager->remove($pricePath, $meta);
-        foreach ($price as $key => $item) {
-            if ($item['value'] == ProductPriceOptionsInterface::VALUE_FIXED) {
-                unset($price[$key]);
+        if ($price) {
+            $meta = $this->arrayManager->remove($pricePath, $meta);
+            foreach ($price as $key => $item) {
+                if ($item['value'] == ProductPriceOptionsInterface::VALUE_FIXED) {
+                    unset($price[$key]);
+                }
             }
+            $meta = $this->arrayManager->merge(
+                $this->arrayManager->slicePath($pricePath, 0, -1),
+                $meta,
+                ['options' => $price]
+            );
         }
-        $meta = $this->arrayManager->merge(
-            $this->arrayManager->slicePath($pricePath, 0, -1),
-            $meta,
-            ['options' => $price]
-        );
 
         return $meta;
     }
diff --git a/app/code/Magento/Bundle/etc/di.xml b/app/code/Magento/Bundle/etc/di.xml
index 6afd212e1b82bf7487076941893fbbc10a33f8a3..e2a913acd50b783f39f2b75dbe9bfb297727e6ba 100644
--- a/app/code/Magento/Bundle/etc/di.xml
+++ b/app/code/Magento/Bundle/etc/di.xml
@@ -130,5 +130,11 @@
             </argument>
         </arguments>
     </type>
-
+    <type name="Magento\Catalog\Model\Product\Price\SpecialPriceStorage">
+        <arguments>
+            <argument name="allowedProductTypes" xsi:type="array">
+                <item name="2" xsi:type="string">bundle</item>
+            </argument>
+        </arguments>
+    </type>
 </config>
diff --git a/app/code/Magento/Catalog/Api/BasePriceStorageInterface.php b/app/code/Magento/Catalog/Api/BasePriceStorageInterface.php
index b669e5a1e8ce80a3eccaf99078d1faf2c84adfcb..399ff340146788f40b8ba209fa5cd36781e7f8ce 100644
--- a/app/code/Magento/Catalog/Api/BasePriceStorageInterface.php
+++ b/app/code/Magento/Catalog/Api/BasePriceStorageInterface.php
@@ -13,18 +13,25 @@ namespace Magento\Catalog\Api;
 interface BasePriceStorageInterface
 {
     /**
-     * Return product prices.
+     * Return product prices. In case of at least one of skus is not found exception will be thrown.
      *
      * @param string[] $skus
      * @return \Magento\Catalog\Api\Data\BasePriceInterface[]
+     * @throws \Magento\Framework\Exception\NoSuchEntityException
      */
     public function get(array $skus);
 
     /**
      * Add or update product prices.
+     * Input item should correspond \Magento\Catalog\Api\Data\CostInterface.
+     * If any items will have invalid price, store id or sku, they will be marked as failed and excluded from
+     * update list and \Magento\Catalog\Api\Data\PriceUpdateResultInterface[] with problem description will be returned.
+     * If there were no failed items during update empty array will be returned.
+     * If error occurred during the update exception will be thrown.
      *
      * @param \Magento\Catalog\Api\Data\BasePriceInterface[] $prices
-     * @return bool Will returned True if updated.
+     * @return \Magento\Catalog\Api\Data\PriceUpdateResultInterface[]
+     * @throws \Magento\Framework\Exception\CouldNotSaveException
      */
     public function update(array $prices);
 }
diff --git a/app/code/Magento/Catalog/Api/CostStorageInterface.php b/app/code/Magento/Catalog/Api/CostStorageInterface.php
index 31055227ad1f2e4de488a7b70dea79eac52d0bb9..94a5c42732a2a1e63718af8d170dd1c7b442b7d1 100644
--- a/app/code/Magento/Catalog/Api/CostStorageInterface.php
+++ b/app/code/Magento/Catalog/Api/CostStorageInterface.php
@@ -13,26 +13,34 @@ namespace Magento\Catalog\Api;
 interface CostStorageInterface
 {
     /**
-     * Return product prices.
+     * Return product prices. In case of at least one of skus is not found exception will be thrown.
      *
      * @param string[] $skus
      * @return \Magento\Catalog\Api\Data\CostInterface[]
+     * @throws \Magento\Framework\Exception\NoSuchEntityException
      */
     public function get(array $skus);
 
     /**
      * Add or update product cost.
+     * Input item should correspond to \Magento\Catalog\Api\Data\CostInterface.
+     * If any items will have invalid cost, store id or sku, they will be marked as failed and excluded from
+     * update list and \Magento\Catalog\Api\Data\PriceUpdateResultInterface[] with problem description will be returned.
+     * If there were no failed items during update empty array will be returned.
+     * If error occurred during the update exception will be thrown.
      *
      * @param \Magento\Catalog\Api\Data\CostInterface[] $prices
-     * @return bool Will returned True if updated.
+     * @return \Magento\Catalog\Api\Data\PriceUpdateResultInterface[]
      */
     public function update(array $prices);
 
     /**
-     * Delete product cost.
+     * Delete product cost. In case of at least one of skus is not found exception will be thrown.
+     * If error occurred during the delete exception will be thrown.
      *
      * @param string[] $skus
-     * @return bool Will returned True if deleted.
+     * @return bool Will return True if deleted.
+     * @throws \Magento\Framework\Exception\NoSuchEntityException
      * @throws \Magento\Framework\Exception\CouldNotDeleteException
      */
     public function delete(array $skus);
diff --git a/app/code/Magento/Catalog/Api/Data/PriceUpdateResultInterface.php b/app/code/Magento/Catalog/Api/Data/PriceUpdateResultInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..216f649efe86d7e5d2b1cf3245f8a4f4bfd49ffd
--- /dev/null
+++ b/app/code/Magento/Catalog/Api/Data/PriceUpdateResultInterface.php
@@ -0,0 +1,69 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Api\Data;
+
+/**
+ * Interface returned in case of incorrect price passed to efficient price API.
+ * @api
+ */
+interface PriceUpdateResultInterface extends \Magento\Framework\Api\ExtensibleDataInterface
+{
+    /**#@+
+     * Constants
+     */
+    const MESSAGE = 'message';
+    const PARAMETERS = 'parameters';
+    /**#@-*/
+
+    /**
+     * Get error message, that contains description of error occurred during price update.
+     *
+     * @return string
+     */
+    public function getMessage();
+
+    /**
+     * Set error message, that contains description of error occurred during price update.
+     *
+     * @param string $message
+     * @return $this
+     */
+    public function setMessage($message);
+
+    /**
+     * Get parameters, that could be displayed in error message placeholders.
+     *
+     * @return string[]
+     */
+    public function getParameters();
+
+    /**
+     * Set parameters, that could be displayed in error message placeholders.
+     *
+     * @param string[] $parameters
+     * @return $this
+     */
+    public function setParameters(array $parameters);
+
+    /**
+     * Retrieve existing extension attributes object.
+     * If extension attributes do not exist return null.
+     *
+     * @return \Magento\Catalog\Api\Data\PriceUpdateResultExtensionInterface|null
+     */
+    public function getExtensionAttributes();
+
+    /**
+     * Set an extension attributes object.
+     *
+     * @param \Magento\Catalog\Api\Data\PriceUpdateResultExtensionInterface $extensionAttributes
+     * @return $this
+     */
+    public function setExtensionAttributes(
+        \Magento\Catalog\Api\Data\PriceUpdateResultExtensionInterface $extensionAttributes
+    );
+}
diff --git a/app/code/Magento/Catalog/Api/Data/SpecialPriceInterface.php b/app/code/Magento/Catalog/Api/Data/SpecialPriceInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..30f6b65106ac4dced8750bbfd3226a14a6c84ea1
--- /dev/null
+++ b/app/code/Magento/Catalog/Api/Data/SpecialPriceInterface.php
@@ -0,0 +1,117 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Api\Data;
+
+/**
+ * Product Special Price Interface is used to encapsulate data that can be processed by efficient price API.
+ * @api
+ */
+interface SpecialPriceInterface extends \Magento\Framework\Api\ExtensibleDataInterface
+{
+    /**#@+
+     * Constants
+     */
+    const PRICE = 'price';
+    const STORE_ID = 'store_id';
+    const SKU = 'sku';
+    const PRICE_FROM = 'price_from';
+    const PRICE_TO = 'price_to';
+    /**#@-*/
+
+    /**
+     * Set product special price value.
+     *
+     * @param float $price
+     * @return $this
+     */
+    public function setPrice($price);
+
+    /**
+     * Get product special price value.
+     *
+     * @return float
+     */
+    public function getPrice();
+
+    /**
+     * Set ID of store, that contains special price value.
+     *
+     * @param int $storeId
+     * @return $this
+     */
+    public function setStoreId($storeId);
+
+    /**
+     * Get ID of store, that contains special price value.
+     *
+     * @return int
+     */
+    public function getStoreId();
+
+    /**
+     * Set SKU of product, that contains special price value.
+     *
+     * @param string $sku
+     * @return $this
+     */
+    public function setSku($sku);
+
+    /**
+     * Get SKU of product, that contains special price value.
+     *
+     * @return string
+     */
+    public function getSku();
+
+    /**
+     * Set start date for special price in Y-m-d H:i:s format.
+     *
+     * @param string $datetime
+     * @return $this
+     */
+    public function setPriceFrom($datetime);
+
+    /**
+     * Get start date for special price in Y-m-d H:i:s format.
+     *
+     * @return string
+     */
+    public function getPriceFrom();
+
+    /**
+     * Set end date for special price in Y-m-d H:i:s format.
+     *
+     * @param string $datetime
+     * @return $this
+     */
+    public function setPriceTo($datetime);
+
+    /**
+     * Get end date for special price in Y-m-d H:i:s format.
+     *
+     * @return string
+     */
+    public function getPriceTo();
+
+    /**
+     * Retrieve existing extension attributes object.
+     * If extension attributes do not exist return null.
+     *
+     * @return \Magento\Catalog\Api\Data\SpecialPriceExtensionInterface|null
+     */
+    public function getExtensionAttributes();
+
+    /**
+     * Set an extension attributes object.
+     *
+     * @param \Magento\Catalog\Api\Data\SpecialPriceExtensionInterface $extensionAttributes
+     * @return $this
+     */
+    public function setExtensionAttributes(
+        \Magento\Catalog\Api\Data\SpecialPriceExtensionInterface $extensionAttributes
+    );
+}
diff --git a/app/code/Magento/Catalog/Api/SpecialPriceInterface.php b/app/code/Magento/Catalog/Api/SpecialPriceInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..971656da1fbfc97bca76d4f576bf540e7350df47
--- /dev/null
+++ b/app/code/Magento/Catalog/Api/SpecialPriceInterface.php
@@ -0,0 +1,67 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Api;
+
+/**
+ * Special prices resource model.
+ * @api
+ */
+interface SpecialPriceInterface
+{
+    /**
+     * Get product special prices by SKUs.
+     *
+     * @param string[] $skus Array containing SKUs
+     *     $skus = [
+     *         'sku value 1',
+     *         'sku value 2'
+     *     ];
+     * @return [
+     *      'entity_id' => (int) Entity identified or entity link field.
+     *      'value' => (float) Special price value.
+     *      'store_id' => (int) Store Id.
+     *      'sku' => (string) Product SKU.
+     *      'price_from' => (string) Special price from date value in UTC.
+     *      'price_to' => (string) Special price to date value in UTC.
+     * ]
+     */
+    public function get(array $skus);
+
+    /**
+     * Update product special prices.
+     *
+     * @param array $prices
+     *      $prices = [
+     *          'entity_id' => (int) Entity identified or entity link field. Required.
+     *          'attribute_id' => (int) Special price attribute Id. Required.
+     *          'store_id' => (int) Store Id. Required.
+     *          'value' => (float) Special price value. Required.
+     *          'price_from' => (string) Special price from date value in Y-m-d H:i:s format in UTC. Optional.
+     *          'price_to' => (string) Special price to date value in Y-m-d H:i:s format in UTC. Optional.
+     *      ];
+     * @return bool
+     * @throws \Magento\Framework\Exception\CouldNotSaveException Thrown if error occurred during price save.
+     */
+    public function update(array $prices);
+
+    /**
+     * Delete product special prices.
+     *
+     * @param array $prices
+     *      $prices = [
+     *          'entity_id' => (int) Entity identified or entity link field. Required.
+     *          'attribute_id' => (int) Special price attribute Id. Required.
+     *          'store_id' => (int) Store Id. Required.
+     *          'value' => (float) Special price value. Required.
+     *          'price_from' => (string) Special price from date value in Y-m-d H:i:s format in UTC. Optional.
+     *          'price_to' => (string) Special price to date value in Y-m-d H:i:s format in UTC. Optional.
+     *      ];
+     * @return bool
+     * @throws \Magento\Framework\Exception\CouldNotDeleteException Thrown if error occurred during price delete.
+     */
+    public function delete(array $prices);
+}
diff --git a/app/code/Magento/Catalog/Api/SpecialPriceStorageInterface.php b/app/code/Magento/Catalog/Api/SpecialPriceStorageInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..b59880f0ed93dd8a510ed00f04bcbe8cd6b0f5e5
--- /dev/null
+++ b/app/code/Magento/Catalog/Api/SpecialPriceStorageInterface.php
@@ -0,0 +1,49 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Api;
+
+/**
+ * Special price storage presents efficient price API and is used to retrieve, update or delete special prices.
+ * @api
+ */
+interface SpecialPriceStorageInterface
+{
+    /**
+     * Return product's special price. In case of at least one of skus is not found exception will be thrown.
+     *
+     * @param string[] $skus
+     * @return \Magento\Catalog\Api\Data\SpecialPriceInterface[]
+     * @throws \Magento\Framework\Exception\NoSuchEntityException
+     */
+    public function get(array $skus);
+
+    /**
+     * Add or update product's special price.
+     * If any items will have invalid price, store id, sku or dates, they will be marked as failed and excluded from
+     * update list and \Magento\Catalog\Api\Data\PriceUpdateResultInterface[] with problem description will be returned.
+     * If there were no failed items during update empty array will be returned.
+     * If error occurred during the update exception will be thrown.
+     *
+     * @param \Magento\Catalog\Api\Data\SpecialPriceInterface[] $prices
+     * @return \Magento\Catalog\Api\Data\PriceUpdateResultInterface[]
+     * @throws \Magento\Framework\Exception\CouldNotSaveException
+     */
+    public function update(array $prices);
+
+    /**
+     * Delete product's special price.
+     * If any items will have invalid price, store id, sku or dates, they will be marked as failed and excluded from
+     * delete list and \Magento\Catalog\Api\Data\PriceUpdateResultInterface[] with problem description will be returned.
+     * If there were no failed items during update empty array will be returned.
+     * If error occurred during the delete exception will be thrown.
+     *
+     * @param \Magento\Catalog\Api\Data\SpecialPriceInterface[] $prices
+     * @return \Magento\Catalog\Api\Data\PriceUpdateResultInterface[]
+     * @throws \Magento\Framework\Exception\CouldNotDeleteException
+     */
+    public function delete(array $prices);
+}
diff --git a/app/code/Magento/Catalog/Api/TierPriceStorageInterface.php b/app/code/Magento/Catalog/Api/TierPriceStorageInterface.php
index 14eed58ef3359a1595f16e72465383a142b7d77a..d23eb637dbf26217c1d93338835f6a8f2862aa98 100644
--- a/app/code/Magento/Catalog/Api/TierPriceStorageInterface.php
+++ b/app/code/Magento/Catalog/Api/TierPriceStorageInterface.php
@@ -13,34 +13,50 @@ namespace Magento\Catalog\Api;
 interface TierPriceStorageInterface
 {
     /**
-     * Return product prices.
+     * Return product prices. In case of at least one of skus is not found exception will be thrown.
      *
      * @param string[] $skus
      * @return \Magento\Catalog\Api\Data\TierPriceInterface[]
+     * @throws \Magento\Framework\Exception\NoSuchEntityException
      */
     public function get(array $skus);
 
     /**
      * Add or update product prices.
+     * If any items will have invalid price, price type, website id, sku, customer group or quantity, they will be
+     * marked as failed and excluded from update list and \Magento\Catalog\Api\Data\PriceUpdateResultInterface[]
+     * with problem description will be returned.
+     * If there were no failed items during update empty array will be returned.
+     * If error occurred during the update exception will be thrown.
      *
      * @param \Magento\Catalog\Api\Data\TierPriceInterface[] $prices
-     * @return bool Will returned True if updated.
+     * @return \Magento\Catalog\Api\Data\PriceUpdateResultInterface[]
      */
     public function update(array $prices);
 
     /**
      * Remove existing tier prices and replace them with the new ones.
+     * If any items will have invalid price, price type, website id, sku, customer group or quantity, they will be
+     * marked as failed and excluded from replace list and \Magento\Catalog\Api\Data\PriceUpdateResultInterface[]
+     * with problem description will be returned.
+     * If there were no failed items during update empty array will be returned.
+     * If error occurred during the update exception will be thrown.
      *
      * @param \Magento\Catalog\Api\Data\TierPriceInterface[] $prices
-     * @return bool Will returned True if replaced.
+     * @return \Magento\Catalog\Api\Data\PriceUpdateResultInterface[]
      */
     public function replace(array $prices);
 
     /**
      * Delete product tier prices.
+     * If any items will have invalid price, price type, website id, sku, customer group or quantity, they will be
+     * marked as failed and excluded from delete list and \Magento\Catalog\Api\Data\PriceUpdateResultInterface[]
+     * with problem description will be returned.
+     * If there were no failed items during update empty array will be returned.
+     * If error occurred during the update exception will be thrown.
      *
      * @param \Magento\Catalog\Api\Data\TierPriceInterface[] $prices
-     * @return bool Will returned True if deleted.
+     * @return \Magento\Catalog\Api\Data\PriceUpdateResultInterface[]
      */
     public function delete(array $prices);
 }
diff --git a/app/code/Magento/Catalog/Model/Product/Price/BasePriceStorage.php b/app/code/Magento/Catalog/Model/Product/Price/BasePriceStorage.php
index 50cffd047ccef148654c27fa938f2dc7879c1785..b9ffb332e1cdd93e8c1597880e9a90b66281b2ee 100644
--- a/app/code/Magento/Catalog/Model/Product/Price/BasePriceStorage.php
+++ b/app/code/Magento/Catalog/Model/Product/Price/BasePriceStorage.php
@@ -43,6 +43,21 @@ class BasePriceStorage implements \Magento\Catalog\Api\BasePriceStorageInterface
      */
     private $productRepository;
 
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\Validation\Result
+     */
+    private $validationResult;
+
+    /**
+     * @var PricePersistenceFactory
+     */
+    private $pricePersistenceFactory;
+
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\InvalidSkuChecker
+     */
+    private $invalidSkuChecker;
+
     /**
      * Price type allowed.
      *
@@ -58,19 +73,14 @@ class BasePriceStorage implements \Magento\Catalog\Api\BasePriceStorageInterface
     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
+     * @param \Magento\Catalog\Model\Product\Price\Validation\Result $validationResult
+     * @param \Magento\Catalog\Model\Product\Price\InvalidSkuChecker $invalidSkuChecker
+     * @param array $allowedProductTypes [optional]
      */
     public function __construct(
         PricePersistenceFactory $pricePersistenceFactory,
@@ -78,6 +88,8 @@ class BasePriceStorage implements \Magento\Catalog\Api\BasePriceStorageInterface
         \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator,
         \Magento\Store\Api\StoreRepositoryInterface $storeRepository,
         \Magento\Catalog\Api\ProductRepositoryInterface $productRepository,
+        \Magento\Catalog\Model\Product\Price\Validation\Result $validationResult,
+        \Magento\Catalog\Model\Product\Price\InvalidSkuChecker $invalidSkuChecker,
         array $allowedProductTypes = []
     ) {
         $this->pricePersistenceFactory = $pricePersistenceFactory;
@@ -85,7 +97,9 @@ class BasePriceStorage implements \Magento\Catalog\Api\BasePriceStorageInterface
         $this->productIdLocator = $productIdLocator;
         $this->storeRepository = $storeRepository;
         $this->productRepository = $productRepository;
+        $this->validationResult = $validationResult;
         $this->allowedProductTypes = $allowedProductTypes;
+        $this->invalidSkuChecker = $invalidSkuChecker;
     }
 
     /**
@@ -93,7 +107,11 @@ class BasePriceStorage implements \Magento\Catalog\Api\BasePriceStorageInterface
      */
     public function get(array $skus)
     {
-        $this->validateSkus($skus);
+        $this->invalidSkuChecker->isSkuListValid(
+            $skus,
+            $this->allowedProductTypes,
+            $this->priceTypeAllowed
+        );
         $rawPrices = $this->getPricePersistence()->get($skus);
         $prices = [];
         foreach ($rawPrices as $rawPrice) {
@@ -114,7 +132,7 @@ class BasePriceStorage implements \Magento\Catalog\Api\BasePriceStorageInterface
      */
     public function update(array $prices)
     {
-        $this->validate($prices);
+        $prices = $this->retrieveValidPrices($prices);
         $formattedPrices = [];
 
         foreach ($prices as $price) {
@@ -130,7 +148,7 @@ class BasePriceStorage implements \Magento\Catalog\Api\BasePriceStorageInterface
 
         $this->getPricePersistence()->update($formattedPrices);
 
-        return true;
+        return $this->validationResult->getFailedItems();
     }
 
     /**
@@ -148,80 +166,59 @@ class BasePriceStorage implements \Magento\Catalog\Api\BasePriceStorageInterface
     }
 
     /**
-     * Validate SKU, check product types and skip not existing products.
+     * Retrieve valid prices that do not contain any errors.
      *
-     * @param array $skus
-     * @throws \Magento\Framework\Exception\LocalizedException
-     * @return void
+     * @param \Magento\Catalog\Api\Data\BasePriceInterface[] $prices
+     * @return array
      */
-    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)
+    private function retrieveValidPrices(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);
+        $invalidSkus = $this->invalidSkuChecker->retrieveInvalidSkuList(
+            $skus,
+            $this->allowedProductTypes,
+            $this->priceTypeAllowed
+        );
 
-        foreach ($prices as $price) {
+        foreach ($prices as $id => $price) {
+            if (!$price->getSku() || in_array($price->getSku(), $invalidSkus)) {
+                $this->validationResult->addFailedItem(
+                    $id,
+                    __(
+                        'Invalid attribute %fieldName = %fieldValue.',
+                        ['fieldName' => '%fieldName', 'fieldValue' => '%fieldValue']
+                    ),
+                    ['fieldName' => 'SKU', 'fieldValue' => $price->getSku()]
+                );
+            }
             if (null === $price->getPrice() || $price->getPrice() < 0) {
-                throw new \Magento\Framework\Exception\LocalizedException(
+                $this->validationResult->addFailedItem(
+                    $id,
                     __(
-                        'Invalid attribute %fieldName: %fieldValue.',
-                        [
-                            'fieldName' => 'Price',
-                            'fieldValue' => $price->getPrice()
-                        ]
-                    )
+                        'Invalid attribute %fieldName = %fieldValue.',
+                        ['fieldName' => '%fieldName', 'fieldValue' => '%fieldValue']
+                    ),
+                    ['fieldName' => 'Price', 'fieldValue' => $price->getPrice()]
+                );
+            }
+            try {
+                $this->storeRepository->getById($price->getStoreId());
+            } catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
+                $this->validationResult->addFailedItem(
+                    $id,
+                    __('Requested store is not found.')
                 );
             }
-            $this->storeRepository->getById($price->getStoreId());
         }
+
+        foreach ($this->validationResult->getFailedRowIds() as $id) {
+            unset($prices[$id]);
+        }
+
+        return $prices;
     }
 }
diff --git a/app/code/Magento/Catalog/Model/Product/Price/CostStorage.php b/app/code/Magento/Catalog/Model/Product/Price/CostStorage.php
index ce1fa2e0e6efbc75bca3061e0a0539c5f2559615..d5f9d08e9115ddb72351cde957df3b8dce7bee57 100644
--- a/app/code/Magento/Catalog/Model/Product/Price/CostStorage.php
+++ b/app/code/Magento/Catalog/Model/Product/Price/CostStorage.php
@@ -33,6 +33,16 @@ class CostStorage implements \Magento\Catalog\Api\CostStorageInterface
      */
     private $productIdLocator;
 
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\Validation\Result
+     */
+    private $validationResult;
+
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\InvalidSkuChecker
+     */
+    private $invalidSkuChecker;
+
     /**
      * Allowed product types.
      *
@@ -57,19 +67,25 @@ class CostStorage implements \Magento\Catalog\Api\CostStorageInterface
      * @param \Magento\Catalog\Api\Data\CostInterfaceFactory $costInterfaceFactory
      * @param \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator
      * @param \Magento\Store\Api\StoreRepositoryInterface $storeRepository
-     * @param array $allowedProductTypes
+     * @param \Magento\Catalog\Model\Product\Price\Validation\Result $validationResult
+     * @param \Magento\Catalog\Model\Product\Price\InvalidSkuChecker $invalidSkuChecker
+     * @param array $allowedProductTypes [optional]
      */
     public function __construct(
         PricePersistenceFactory $pricePersistenceFactory,
         \Magento\Catalog\Api\Data\CostInterfaceFactory $costInterfaceFactory,
         \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator,
         \Magento\Store\Api\StoreRepositoryInterface $storeRepository,
+        \Magento\Catalog\Model\Product\Price\Validation\Result $validationResult,
+        \Magento\Catalog\Model\Product\Price\InvalidSkuChecker $invalidSkuChecker,
         array $allowedProductTypes = []
     ) {
         $this->pricePersistenceFactory = $pricePersistenceFactory;
         $this->costInterfaceFactory = $costInterfaceFactory;
         $this->productIdLocator = $productIdLocator;
         $this->storeRepository = $storeRepository;
+        $this->validationResult = $validationResult;
+        $this->invalidSkuChecker = $invalidSkuChecker;
         $this->allowedProductTypes = $allowedProductTypes;
     }
 
@@ -78,7 +94,7 @@ class CostStorage implements \Magento\Catalog\Api\CostStorageInterface
      */
     public function get(array $skus)
     {
-        $this->validateSkus($skus);
+        $this->invalidSkuChecker->isSkuListValid($skus, $this->allowedProductTypes);
         $rawPrices = $this->getPricePersistence()->get($skus);
         $prices = [];
         foreach ($rawPrices as $rawPrice) {
@@ -99,12 +115,13 @@ class CostStorage implements \Magento\Catalog\Api\CostStorageInterface
      */
     public function update(array $prices)
     {
-        $this->validate($prices);
+        $prices = $this->retrieveValidPrices($prices);
         $formattedPrices = [];
 
         foreach ($prices as $price) {
-            $ids = array_keys($this->productIdLocator->retrieveProductIdsBySkus([$price->getSku()])[$price->getSku()]);
-            foreach ($ids as $id) {
+            $productIdsBySkus = $this->productIdLocator->retrieveProductIdsBySkus([$price->getSku()]);
+            $productIds = array_keys($productIdsBySkus[$price->getSku()]);
+            foreach ($productIds as $id) {
                 $formattedPrices[] = [
                     'store_id' => $price->getStoreId(),
                     $this->getPricePersistence()->getEntityLinkField() => $id,
@@ -115,7 +132,7 @@ class CostStorage implements \Magento\Catalog\Api\CostStorageInterface
 
         $this->getPricePersistence()->update($formattedPrices);
 
-        return true;
+        return $this->validationResult->getFailedItems();
     }
 
     /**
@@ -123,7 +140,7 @@ class CostStorage implements \Magento\Catalog\Api\CostStorageInterface
      */
     public function delete(array $skus)
     {
-        $this->validateSkus($skus);
+        $this->invalidSkuChecker->isSkuListValid($skus, $this->allowedProductTypes);
         $this->getPricePersistence()->delete($skus);
 
         return true;
@@ -144,75 +161,55 @@ class CostStorage implements \Magento\Catalog\Api\CostStorageInterface
     }
 
     /**
-     * Validate that prices have appropriate values.
+     * Retrieve valid prices that do not contain any errors.
      *
      * @param array $prices
-     * @throws \Magento\Framework\Exception\LocalizedException
-     * @return void
+     * @return array
      */
-    private function validate(array $prices)
+    private function retrieveValidPrices(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);
+        $invalidSkus = $this->invalidSkuChecker->retrieveInvalidSkuList($skus, $this->allowedProductTypes);
 
-        foreach ($prices as $price) {
+        foreach ($prices as $id => $price) {
+            if (!$price->getSku() || in_array($price->getSku(), $invalidSkus)) {
+                $this->validationResult->addFailedItem(
+                    $id,
+                    __(
+                        'Invalid attribute %fieldName = %fieldValue.',
+                        ['fieldName' => '%fieldName', 'fieldValue' => '%fieldValue']
+                    ),
+                    ['fieldName' => 'SKU', 'fieldValue' => $price->getSku()]
+                );
+            }
             if (null === $price->getCost() || $price->getCost() < 0) {
-                throw new \Magento\Framework\Exception\LocalizedException(
+                $this->validationResult->addFailedItem(
+                    $id,
                     __(
-                        'Invalid attribute %fieldName: %fieldValue.',
-                        [
-                            'fieldName' => 'Cost',
-                            'fieldValue' => $price->getCost()
-                        ]
-                    )
+                        'Invalid attribute %fieldName = %fieldValue.',
+                        ['fieldName' => '%fieldName', 'fieldValue' => '%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;
-                }
+            try {
+                $this->storeRepository->getById($price->getStoreId());
+            } catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
+                $this->validationResult->addFailedItem(
+                    $id,
+                    __('Requested store is not found.')
+                );
             }
         }
 
-        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);
+        foreach ($this->validationResult->getFailedRowIds() as $id) {
+            unset($prices[$id]);
         }
+
+        return $prices;
     }
 }
diff --git a/app/code/Magento/Catalog/Model/Product/Price/InvalidSkuChecker.php b/app/code/Magento/Catalog/Model/Product/Price/InvalidSkuChecker.php
new file mode 100644
index 0000000000000000000000000000000000000000..2b8fa682b48facfac7961d818aee209c957cf00b
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/Product/Price/InvalidSkuChecker.php
@@ -0,0 +1,81 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Model\Product\Price;
+
+/**
+ * Class is responsible to detect list of invalid SKU values from list of provided skus and allowed product types.
+ */
+class InvalidSkuChecker
+{
+    /**
+     * @param \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator
+     * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository
+     */
+    public function __construct(
+        \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator,
+        \Magento\Catalog\Api\ProductRepositoryInterface $productRepository
+    ) {
+        $this->productIdLocator = $productIdLocator;
+        $this->productRepository = $productRepository;
+    }
+
+    /**
+     * Retrieve not found or invalid SKUs and and check that their type corresponds to allowed types list.
+     *
+     * @param array $skus
+     * @param array $allowedProductTypes
+     * @param int|bool $allowedPriceTypeValue
+     * @return array
+     */
+    public function retrieveInvalidSkuList(array $skus, array $allowedProductTypes, $allowedPriceTypeValue = false)
+    {
+        $idsBySku = $this->productIdLocator->retrieveProductIdsBySkus($skus);
+        $skuDiff = array_diff($skus, array_keys($idsBySku));
+
+        foreach ($idsBySku as $sku => $ids) {
+            foreach ($ids as $type) {
+                $valueTypeIsAllowed = false;
+
+                if ($allowedPriceTypeValue
+                    && $type == \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE
+                    && $this->productRepository->get($sku)->getPriceType() != $allowedProductTypes
+                ) {
+                    $valueTypeIsAllowed = true;
+                }
+
+                if (!in_array($type, $allowedProductTypes) || $valueTypeIsAllowed) {
+                    $skuDiff[] = $sku;
+                    break;
+                }
+            }
+        }
+
+        return $skuDiff;
+    }
+
+    /**
+     * Check that SKU list is valid or return exception if it contains invalid values.
+     *
+     * @param array $skus
+     * @param array $allowedProductTypes
+     * @param int|bool $allowedPriceTypeValue
+     * @return void
+     * @throws \Magento\Framework\Exception\NoSuchEntityException
+     */
+    public function isSkuListValid(array $skus, array $allowedProductTypes, $allowedPriceTypeValue = false)
+    {
+        $failedItems = $this->retrieveInvalidSkuList($skus, $allowedProductTypes, $allowedPriceTypeValue);
+
+        if (!empty($failedItems)) {
+            $values = implode(', ', $failedItems);
+            $description = count($failedItems) == 1
+                ? __('Requested product doesn\'t exist: %sku', ['sku' => $values])
+                : __('Requested products don\'t exist: %sku', ['sku' => $values]);
+            throw new \Magento\Framework\Exception\NoSuchEntityException($description);
+        }
+    }
+}
diff --git a/app/code/Magento/Catalog/Model/Product/Price/PriceUpdateResult.php b/app/code/Magento/Catalog/Model/Product/Price/PriceUpdateResult.php
new file mode 100644
index 0000000000000000000000000000000000000000..c94f30512176471ad94defb63137173a4cdf5414
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/Product/Price/PriceUpdateResult.php
@@ -0,0 +1,64 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Model\Product\Price;
+
+use Magento\Catalog\Api\Data\PriceUpdateResultInterface;
+
+/**
+ * {@inheritdoc}
+ */
+class PriceUpdateResult extends \Magento\Framework\Model\AbstractExtensibleModel implements PriceUpdateResultInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getMessage()
+    {
+        return $this->getData(self::MESSAGE);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setMessage($message)
+    {
+        return $this->setData(self::MESSAGE, $message);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getParameters()
+    {
+        return $this->getData(self::PARAMETERS);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setParameters(array $parameters)
+    {
+        return $this->setData(self::PARAMETERS, $parameters);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getExtensionAttributes()
+    {
+        return $this->_getExtensionAttributes();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setExtensionAttributes(
+        \Magento\Catalog\Api\Data\PriceUpdateResultExtensionInterface $extensionAttributes
+    ) {
+        return $this->_setExtensionAttributes($extensionAttributes);
+    }
+}
diff --git a/app/code/Magento/Catalog/Model/Product/Price/SpecialPrice.php b/app/code/Magento/Catalog/Model/Product/Price/SpecialPrice.php
new file mode 100644
index 0000000000000000000000000000000000000000..591252dcf441d39dbf7da492cb7f231354b77219
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/Product/Price/SpecialPrice.php
@@ -0,0 +1,112 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Model\Product\Price;
+
+use Magento\Catalog\Api\Data\SpecialPriceInterface;
+
+/**
+ * Product Special Price class is used to encapsulate data that can be processed by efficient price API.
+ */
+class SpecialPrice extends \Magento\Framework\Model\AbstractExtensibleModel implements SpecialPriceInterface
+{
+    /**
+     * {@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 setPriceFrom($datetime)
+    {
+        return $this->setData(self::PRICE_FROM, $datetime);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getPriceFrom()
+    {
+        return $this->getData(self::PRICE_FROM);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setPriceTo($datetime)
+    {
+        return $this->setData(self::PRICE_TO, $datetime);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getPriceTo()
+    {
+        return $this->getData(self::PRICE_TO);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getExtensionAttributes()
+    {
+        return $this->_getExtensionAttributes();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setExtensionAttributes(
+        \Magento\Catalog\Api\Data\SpecialPriceExtensionInterface $extensionAttributes
+    ) {
+        return $this->_setExtensionAttributes($extensionAttributes);
+    }
+}
diff --git a/app/code/Magento/Catalog/Model/Product/Price/SpecialPriceStorage.php b/app/code/Magento/Catalog/Model/Product/Price/SpecialPriceStorage.php
new file mode 100644
index 0000000000000000000000000000000000000000..dba621d780883271c345f18a5f52168630ed6ff9
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/Product/Price/SpecialPriceStorage.php
@@ -0,0 +1,245 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Model\Product\Price;
+
+use Magento\Framework\Exception\NoSuchEntityException;
+
+/**
+ * Special price storage presents efficient price API and is used to retrieve, update or delete special prices.
+ */
+class SpecialPriceStorage implements \Magento\Catalog\Api\SpecialPriceStorageInterface
+{
+    /**
+     * @var \Magento\Catalog\Api\SpecialPriceInterface
+     */
+    private $specialPriceResource;
+
+    /**
+     * @var \Magento\Catalog\Api\Data\SpecialPriceInterfaceFactory
+     */
+    private $specialPriceFactory;
+
+    /**
+     * @var \Magento\Catalog\Model\ProductIdLocatorInterface
+     */
+    private $productIdLocator;
+
+    /**
+     * @var \Magento\Store\Api\StoreRepositoryInterface
+     */
+    private $storeRepository;
+
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\Validation\Result
+     */
+    private $validationResult;
+
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\InvalidSkuChecker
+     */
+    private $invalidSkuChecker;
+
+    /**
+     * @var array
+     */
+    private $allowedProductTypes = [];
+
+    /**
+     * @param \Magento\Catalog\Api\SpecialPriceInterface $specialPriceResource
+     * @param \Magento\Catalog\Api\Data\SpecialPriceInterfaceFactory $specialPriceFactory
+     * @param \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator
+     * @param \Magento\Store\Api\StoreRepositoryInterface $storeRepository
+     * @param \Magento\Catalog\Model\Product\Price\Validation\Result $validationResult
+     * @param \Magento\Catalog\Model\Product\Price\InvalidSkuChecker $invalidSkuChecker
+     * @param array $allowedProductTypes [optional]
+     */
+    public function __construct(
+        \Magento\Catalog\Api\SpecialPriceInterface $specialPriceResource,
+        \Magento\Catalog\Api\Data\SpecialPriceInterfaceFactory $specialPriceFactory,
+        \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator,
+        \Magento\Store\Api\StoreRepositoryInterface $storeRepository,
+        \Magento\Catalog\Model\Product\Price\Validation\Result $validationResult,
+        \Magento\Catalog\Model\Product\Price\InvalidSkuChecker $invalidSkuChecker,
+        array $allowedProductTypes = []
+    ) {
+        $this->specialPriceResource = $specialPriceResource;
+        $this->specialPriceFactory = $specialPriceFactory;
+        $this->productIdLocator = $productIdLocator;
+        $this->storeRepository = $storeRepository;
+        $this->validationResult = $validationResult;
+        $this->invalidSkuChecker = $invalidSkuChecker;
+        $this->allowedProductTypes = $allowedProductTypes;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function get(array $skus)
+    {
+        $this->invalidSkuChecker->isSkuListValid($skus, $this->allowedProductTypes);
+        $rawPrices = $this->specialPriceResource->get($skus);
+
+        $prices = [];
+        foreach ($rawPrices as $rawPrice) {
+            /** @var \Magento\Catalog\Api\Data\SpecialPriceInterface $price */
+            $price = $this->specialPriceFactory->create();
+            $sku = isset($rawPrice['sku'])
+                ? $rawPrice['sku']
+                : $this->retrieveSkuById($rawPrice[$this->specialPriceResource->getEntityLinkField()], $skus);
+            $price->setSku($sku);
+            $price->setPrice($rawPrice['value']);
+            $price->setStoreId($rawPrice['store_id']);
+            $price->setPriceFrom($rawPrice['price_from']);
+            $price->setPriceTo($rawPrice['price_to']);
+            $prices[] = $price;
+        }
+
+        return $prices;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function update(array $prices)
+    {
+        $prices = $this->retrieveValidPrices($prices);
+        $this->specialPriceResource->update($prices);
+
+        return $this->validationResult->getFailedItems();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function delete(array $prices)
+    {
+        $prices = $this->retrieveValidPrices($prices);
+        $this->specialPriceResource->delete($prices);
+
+        return $this->validationResult->getFailedItems();
+    }
+
+    /**
+     * Retrieve prices with correct values.
+     *
+     * @param array $prices
+     * @return array
+     */
+    private function retrieveValidPrices(array $prices)
+    {
+        $skus = array_unique(
+            array_map(function ($price) {
+                return $price->getSku();
+            }, $prices)
+        );
+        $failedSkus = $this->invalidSkuChecker->retrieveInvalidSkuList($skus, $this->allowedProductTypes);
+
+        foreach ($prices as $key => $price) {
+            if (!$price->getSku() || in_array($price->getSku(), $failedSkus)) {
+                $this->validationResult->addFailedItem(
+                    $key,
+                    __(
+                        'Requested product doesn\'t exist: %sku',
+                        ['sku' => '%sku']
+                    ),
+                    ['sku' => $price->getSku()]
+                );
+            }
+            $this->checkPrice($price->getPrice(), $key);
+            $this->checkDate($price->getPriceFrom(), 'Price From', $key);
+            $this->checkDate($price->getPriceTo(), 'Price To', $key);
+            try {
+                $this->storeRepository->getById($price->getStoreId());
+            } catch (NoSuchEntityException $e) {
+                $this->validationResult->addFailedItem(
+                    $key,
+                    __('Requested store is not found.')
+                );
+            }
+        }
+
+        foreach ($this->validationResult->getFailedRowIds() as $id) {
+            unset($prices[$id]);
+        }
+
+        return $prices;
+    }
+
+    /**
+     * Check that date value is correct and add error to aggregator if it contains incorrect data.
+     *
+     * @param string $value
+     * @param string $label
+     * @param int $key
+     * @return void
+     */
+    private function checkDate($value, $label, $key)
+    {
+        if ($value && !$this->isCorrectDateValue($value)) {
+            $this->validationResult->addFailedItem(
+                $key,
+                __(
+                    'Invalid attribute %fieldName = %fieldValue.',
+                    ['fieldName' => '%fieldName', 'fieldValue' => '%fieldValue']
+                ),
+                ['fieldName' => $label, 'fieldValue' => $value]
+            );
+        }
+    }
+
+    /**
+     * Check that provided price value is not empty and not lower then zero and add error to aggregator if price
+     * contains not valid data.
+     *
+     * @param float $price
+     * @param int $key
+     * @return void
+     */
+    private function checkPrice($price, $key)
+    {
+        if (null === $price || $price < 0) {
+            $this->validationResult->addFailedItem(
+                $key,
+                __(
+                    'Invalid attribute %fieldName = %fieldValue.',
+                    ['fieldName' => '%fieldName', 'fieldValue' => '%fieldValue']
+                ),
+                ['fieldName' => 'Price', 'fieldValue' => $price]
+            );
+        }
+    }
+
+    /**
+     * Retrieve SKU by product ID.
+     *
+     * @param int $id
+     * @param array $skus
+     * @return int|null
+     */
+    private function retrieveSkuById($id, array $skus)
+    {
+        foreach ($this->productIdLocator->retrieveProductIdsBySkus($skus) as $sku => $ids) {
+            if (false !== array_key_exists($id, $ids)) {
+                return $sku;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Check that date value is correct.
+     *
+     * @param string $date
+     * @return bool
+     */
+    private function isCorrectDateValue($date)
+    {
+        $actualDate = date('Y-m-d H:i:s', strtotime($date));
+        return $actualDate && $actualDate === $date;
+    }
+}
diff --git a/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php b/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php
index 58d3804c2dac6bd82d01c2c4842fcdcbeb642494..b8e78380bbc31bcb1f4c1cc525146f6e4314adef 100644
--- a/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php
+++ b/app/code/Magento/Catalog/Model/Product/Price/TierPriceStorage.php
@@ -23,7 +23,7 @@ class TierPriceStorage implements \Magento\Catalog\Api\TierPriceStorageInterface
     /**
      * Tier price validator.
      *
-     * @var \Magento\Catalog\Model\Product\Price\TierPriceValidator
+     * @var \Magento\Catalog\Model\Product\Price\Validation\TierPriceValidator
      */
     private $tierPriceValidator;
 
@@ -70,10 +70,8 @@ class TierPriceStorage implements \Magento\Catalog\Api\TierPriceStorageInterface
     private $indexerChunkValue = 500;
 
     /**
-     * TierPriceStorage constructor.
-     *
      * @param TierPricePersistence $tierPricePersistence
-     * @param TierPriceValidator $tierPriceValidator
+     * @param \Magento\Catalog\Model\Product\Price\Validation\TierPriceValidator $tierPriceValidator
      * @param TierPriceFactory $tierPriceFactory
      * @param \Magento\Catalog\Model\Indexer\Product\Price $priceIndexer
      * @param \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator
@@ -82,7 +80,7 @@ class TierPriceStorage implements \Magento\Catalog\Api\TierPriceStorageInterface
      */
     public function __construct(
         TierPricePersistence $tierPricePersistence,
-        TierPriceValidator $tierPriceValidator,
+        \Magento\Catalog\Model\Product\Price\Validation\TierPriceValidator $tierPriceValidator,
         TierPriceFactory $tierPriceFactory,
         \Magento\Catalog\Model\Indexer\Product\Price $priceIndexer,
         \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator,
@@ -104,16 +102,8 @@ class TierPriceStorage implements \Magento\Catalog\Api\TierPriceStorageInterface
     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;
+        return $this->getExistingPrices($skus);
     }
 
     /**
@@ -127,13 +117,14 @@ class TierPriceStorage implements \Magento\Catalog\Api\TierPriceStorageInterface
                 return $price->getSku();
             }, $prices)
         );
-        $this->tierPriceValidator->validatePrices($prices, $this->get($skus));
+        $result = $this->tierPriceValidator->retrieveValidationResult($prices, $this->getExistingPrices($skus));
+        $prices = $this->removeIncorrectPrices($prices, $result->getFailedRowIds());
         $formattedPrices = $this->retrieveFormattedPrices($prices);
         $this->tierPricePersistence->update($formattedPrices);
         $this->reindexPrices($affectedIds);
         $this->invalidateFullPageCache();
 
-        return true;
+        return $result->getFailedItems();
     }
 
     /**
@@ -141,14 +132,15 @@ class TierPriceStorage implements \Magento\Catalog\Api\TierPriceStorageInterface
      */
     public function replace(array $prices)
     {
-        $this->tierPriceValidator->validatePrices($prices);
+        $result = $this->tierPriceValidator->retrieveValidationResult($prices);
+        $prices = $this->removeIncorrectPrices($prices, $result->getFailedRowIds());
         $affectedIds = $this->retrieveAffectedProductIdsForPrices($prices);
         $formattedPrices = $this->retrieveFormattedPrices($prices);
         $this->tierPricePersistence->replace($formattedPrices, $affectedIds);
         $this->reindexPrices($affectedIds);
         $this->invalidateFullPageCache();
 
-        return true;
+        return $result->getFailedItems();
     }
 
     /**
@@ -157,13 +149,34 @@ class TierPriceStorage implements \Magento\Catalog\Api\TierPriceStorageInterface
     public function delete(array $prices)
     {
         $affectedIds = $this->retrieveAffectedProductIdsForPrices($prices);
-        $this->tierPriceValidator->validatePrices($prices);
+        $result = $this->tierPriceValidator->retrieveValidationResult($prices);
+        $prices = $this->removeIncorrectPrices($prices, $result->getFailedRowIds());
         $priceIds = $this->retrieveAffectedPriceIds($prices);
         $this->tierPricePersistence->delete($priceIds);
         $this->reindexPrices($affectedIds);
         $this->invalidateFullPageCache();
 
-        return true;
+        return $result->getFailedItems();
+    }
+
+    /**
+     * Get existing prices by SKUs.
+     *
+     * @param array $skus
+     * @return array
+     */
+    private function getExistingPrices(array $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;
     }
 
     /**
@@ -242,11 +255,11 @@ class TierPriceStorage implements \Magento\Catalog\Api\TierPriceStorageInterface
     }
 
     /**
-     * Retrieve price ID.
+     * Look through provided price in list of existing prices and retrieve it's Id.
      *
      * @param array $price
      * @param array $existingPrices
-     * @return int
+     * @return int|void
      */
     private function retrievePriceId(array $price, array $existingPrices)
     {
@@ -265,7 +278,7 @@ class TierPriceStorage implements \Magento\Catalog\Api\TierPriceStorageInterface
     }
 
     /**
-     * Check is correct price value
+     * Check that price value or price percentage value is not equal to 0 and is not similar with existing value.
      *
      * @param array $existingPrice
      * @param array $price
@@ -320,4 +333,20 @@ class TierPriceStorage implements \Magento\Catalog\Api\TierPriceStorageInterface
             $this->priceIndexer->execute($affectedIds);
         }
     }
+
+    /**
+     * Remove prices from price list by id list.
+     *
+     * @param array $prices
+     * @param array $ids
+     * @return array
+     */
+    private function removeIncorrectPrices(array $prices, array $ids)
+    {
+        foreach ($ids as $id) {
+            unset($prices[$id]);
+        }
+
+        return $prices;
+    }
 }
diff --git a/app/code/Magento/Catalog/Model/Product/Price/TierPriceValidator.php b/app/code/Magento/Catalog/Model/Product/Price/TierPriceValidator.php
deleted file mode 100644
index 8a307d04fbfbecc705193832edd9b539467be62a..0000000000000000000000000000000000000000
--- a/app/code/Magento/Catalog/Model/Product/Price/TierPriceValidator.php
+++ /dev/null
@@ -1,351 +0,0 @@
-<?php
-/**
- * Copyright © 2013-2017 Magento, Inc. 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/Price/Validation/Result.php b/app/code/Magento/Catalog/Model/Product/Price/Validation/Result.php
new file mode 100644
index 0000000000000000000000000000000000000000..3ec7ee7abe3a78f474fc32529ba2528c73098a33
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/Product/Price/Validation/Result.php
@@ -0,0 +1,82 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Model\Product\Price\Validation;
+
+/**
+ * Validation Result is used to aggregate errors that occurred during price update.
+ */
+class Result
+{
+    /**
+     * @var \Magento\Catalog\Api\Data\PriceUpdateResultInterfaceFactory
+     */
+    private $priceUpdateResultFactory;
+
+    /**
+     * Failed items.
+     *
+     * @var array
+     */
+    private $failedItems = [];
+
+    /**
+     * @param \Magento\Catalog\Api\Data\PriceUpdateResultInterfaceFactory $priceUpdateResultFactory
+     */
+    public function __construct(
+        \Magento\Catalog\Api\Data\PriceUpdateResultInterfaceFactory $priceUpdateResultFactory
+    ) {
+        $this->priceUpdateResultFactory = $priceUpdateResultFactory;
+    }
+
+    /**
+     * Add failed price identified, message and message parameters, that occurred during price update.
+     *
+     * @param int $id Failed price identified.
+     * @param string $message Failure reason message.
+     * @param array $parameters (optional). Placeholder values in ['placeholder key' => 'placeholder value'] format
+     * for failure reason message.
+     * @return void
+     */
+    public function addFailedItem($id, $message, array $parameters = [])
+    {
+        $this->failedItems[$id][] = [
+            'message' => $message,
+            'parameters' => $parameters
+        ];
+    }
+
+    /**
+     * Get ids of rows, that contained errors during price update.
+     *
+     * @return int[]
+     */
+    public function getFailedRowIds()
+    {
+        return $this->failedItems ? array_keys($this->failedItems) : [];
+    }
+
+    /**
+     * Get price update errors, that occurred during price update.
+     *
+     * @return \Magento\Catalog\Api\Data\PriceUpdateResultInterface[]
+     */
+    public function getFailedItems()
+    {
+        $failedItems = [];
+
+        foreach ($this->failedItems as $items) {
+            foreach ($items as $failedRecord) {
+                $resultItem = $this->priceUpdateResultFactory->create();
+                $resultItem->setMessage($failedRecord['message']);
+                $resultItem->setParameters($failedRecord['parameters']);
+                $failedItems[] = $resultItem;
+            }
+        }
+
+        return $failedItems;
+    }
+}
diff --git a/app/code/Magento/Catalog/Model/Product/Price/Validation/TierPriceValidator.php b/app/code/Magento/Catalog/Model/Product/Price/Validation/TierPriceValidator.php
new file mode 100644
index 0000000000000000000000000000000000000000..76da4b7011bd521f06c497856c11ee465f32d297
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/Product/Price/Validation/TierPriceValidator.php
@@ -0,0 +1,414 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Model\Product\Price\Validation;
+
+/**
+ * Tier Price Validator.
+ */
+class TierPriceValidator
+{
+    /**
+     * @var \Magento\Catalog\Model\ProductIdLocatorInterface
+     */
+    private $productIdLocator;
+
+    /**
+     * @var \Magento\Framework\Api\SearchCriteriaBuilder
+     */
+    private $searchCriteriaBuilder;
+
+    /**
+     * @var \Magento\Framework\Api\FilterBuilder
+     */
+    private $filterBuilder;
+
+    /**
+     * @var \Magento\Customer\Api\GroupRepositoryInterface
+     */
+    private $customerGroupRepository;
+
+    /**
+     * @var \Magento\Store\Api\WebsiteRepositoryInterface
+     */
+    private $websiteRepository;
+
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\Validation\Result
+     */
+    private $validationResult;
+
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\TierPricePersistence
+     */
+    private $tierPricePersistence;
+
+    /**
+     * Groups by code cache.
+     *
+     * @var array
+     */
+    private $customerGroupsByCode = [];
+
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\InvalidSkuChecker
+     */
+    private $invalidSkuChecker;
+
+    /**
+     * 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 \Magento\Catalog\Model\Product\Price\TierPricePersistence $tierPricePersistence
+     * @param \Magento\Catalog\Model\Product\Price\Validation\Result $validationResult
+     * @param \Magento\Catalog\Model\Product\Price\InvalidSkuChecker $invalidSkuChecker
+     * @param array $allowedProductTypes [optional]
+     */
+    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,
+        \Magento\Catalog\Model\Product\Price\TierPricePersistence $tierPricePersistence,
+        \Magento\Catalog\Model\Product\Price\Validation\Result $validationResult,
+        \Magento\Catalog\Model\Product\Price\InvalidSkuChecker $invalidSkuChecker,
+        array $allowedProductTypes = []
+    ) {
+        $this->productIdLocator = $productIdLocator;
+        $this->searchCriteriaBuilder = $searchCriteriaBuilder;
+        $this->filterBuilder = $filterBuilder;
+        $this->customerGroupRepository = $customerGroupRepository;
+        $this->websiteRepository = $websiteRepository;
+        $this->tierPricePersistence = $tierPricePersistence;
+        $this->validationResult = $validationResult;
+        $this->invalidSkuChecker = $invalidSkuChecker;
+        $this->allowedProductTypes = $allowedProductTypes;
+    }
+
+    /**
+     * Validate SKU.
+     *
+     * @param array $skus
+     * @throws \Magento\Framework\Exception\NoSuchEntityException
+     * @return void
+     */
+    public function validateSkus(array $skus)
+    {
+        $this->invalidSkuChecker->isSkuListValid($skus, $this->allowedProductTypes);
+    }
+
+    /**
+     * Validate that prices have appropriate values and are unique and return result.
+     *
+     * @param array $prices
+     * @param array $existingPrices
+     * @return \Magento\Catalog\Model\Product\Price\Validation\Result $validationResult
+     */
+    public function retrieveValidationResult(array $prices, array $existingPrices = [])
+    {
+        $validationResult = clone $this->validationResult;
+        $skus = array_unique(
+            array_map(function ($price) {
+                return $price->getSku();
+            }, $prices)
+        );
+        $skuDiff = $this->invalidSkuChecker->retrieveInvalidSkuList($skus, $this->allowedProductTypes);
+        $idsBySku = $this->productIdLocator->retrieveProductIdsBySkus($skus);
+
+        $pricesBySku = [];
+
+        foreach ($prices as $price) {
+            $pricesBySku[$price->getSku()][] = $price;
+        }
+
+        foreach ($prices as $key => $price) {
+            $this->checkSku($price, $key, $skuDiff, $validationResult);
+            $this->checkPrice($price, $key, $validationResult);
+            $ids = isset($idsBySku[$price->getSku()]) ? $idsBySku[$price->getSku()] : [];
+            $this->checkPriceType($price, $ids, $key, $validationResult);
+            $this->checkQuantity($price, $key, $validationResult);
+            $this->checkWebsite($price, $key, $validationResult);
+            if (isset($pricesBySku[$price->getSku()])) {
+                $this->checkUnique($price, $pricesBySku[$price->getSku()], $key, $validationResult);
+            }
+            $this->checkUnique($price, $existingPrices, $key, $validationResult);
+            $this->checkGroup($price, $key, $validationResult);
+        }
+
+        return $validationResult;
+    }
+
+    /**
+     * Check that sku value is correct.
+     *
+     * @param \Magento\Catalog\Api\Data\TierPriceInterface $price
+     * @param int $key
+     * @param array $invalidSkus
+     * @param Result $validationResult
+     * @return void
+     */
+    private function checkSku(
+        \Magento\Catalog\Api\Data\TierPriceInterface $price,
+        $key,
+        array $invalidSkus,
+        Result $validationResult
+    ) {
+        if (!$price->getSku() || in_array($price->getSku(), $invalidSkus)) {
+            $validationResult->addFailedItem(
+                $key,
+                __(
+                    'Invalid attribute %fieldName = %fieldValue.',
+                    ['fieldName' => '%fieldName', 'fieldValue' => '%fieldValue']
+                ),
+                ['fieldName' => 'SKU', 'fieldValue' => $price->getSku()]
+            );
+        }
+    }
+
+    /**
+     * Verify that price value is correct.
+     *
+     * @param \Magento\Catalog\Api\Data\TierPriceInterface $price
+     * @param int $key
+     * @param Result $validationResult
+     * @return void
+     */
+    private function checkPrice(\Magento\Catalog\Api\Data\TierPriceInterface $price, $key, Result $validationResult)
+    {
+        if (
+            null === $price->getPrice()
+            || $price->getPrice() < 0
+            || ($price->getPriceType() === \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_DISCOUNT
+                && $price->getPrice() > 100
+            )
+        ) {
+            $validationResult->addFailedItem(
+                $key,
+                __(
+                    'Invalid attribute %fieldName = %fieldValue.',
+                    ['fieldName' => '%fieldName', 'fieldValue' => '%fieldValue']
+                ),
+                ['fieldName' => 'Price', 'fieldValue' => $price->getPrice()]
+            );
+        }
+    }
+
+    /**
+     * Verify that price type is correct.
+     *
+     * @param \Magento\Catalog\Api\Data\TierPriceInterface $price
+     * @param array $ids
+     * @param int $key
+     * @param Result $validationResult
+     * @return void
+     */
+    private function checkPriceType(
+        \Magento\Catalog\Api\Data\TierPriceInterface $price,
+        array $ids,
+        $key,
+        Result $validationResult
+    ) {
+        if (
+            !in_array(
+                $price->getPriceType(),
+                [
+                    \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_FIXED,
+                    \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_DISCOUNT
+                ]
+            )
+            || (array_search(\Magento\Catalog\Model\Product\Type::TYPE_BUNDLE, $ids)
+                && $price->getPriceType() !== \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_DISCOUNT)
+        ) {
+            $validationResult->addFailedItem(
+                $key,
+                __(
+                    'Invalid attribute %fieldName = %fieldValue.',
+                    ['fieldName' => '%fieldName', 'fieldValue' => '%fieldValue']
+                ),
+                ['fieldName' => 'Price Type', 'fieldValue' => $price->getPriceType()]
+            );
+        }
+    }
+
+    /**
+     * Verify that product quantity is correct.
+     *
+     * @param \Magento\Catalog\Api\Data\TierPriceInterface $price
+     * @param int $key
+     * @param Result $validationResult
+     * @return void
+     */
+    private function checkQuantity(\Magento\Catalog\Api\Data\TierPriceInterface $price, $key, Result $validationResult)
+    {
+        if ($price->getQuantity() < 1) {
+            $validationResult->addFailedItem(
+                $key,
+                __(
+                    'Invalid attribute %fieldName = %fieldValue.',
+                    ['fieldName' => '%fieldName', 'fieldValue' => '%fieldValue']
+                ),
+                ['fieldName' => 'Quantity', 'fieldValue' => $price->getQuantity()]
+            );
+        }
+    }
+
+    /**
+     * Verify that website exists.
+     *
+     * @param \Magento\Catalog\Api\Data\TierPriceInterface $price
+     * @param int $key
+     * @param Result $validationResult
+     * @return void
+     */
+    private function checkWebsite(\Magento\Catalog\Api\Data\TierPriceInterface $price, $key, Result $validationResult)
+    {
+        try {
+            $this->websiteRepository->getById($price->getWebsiteId());
+        } catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
+            $validationResult->addFailedItem(
+                $key,
+                __(
+                    'Invalid attribute %fieldName = %fieldValue.',
+                    ['fieldName' => '%fieldName', 'fieldValue' => '%fieldValue']
+                ),
+                ['fieldName' => 'Website Id', 'fieldValue' => $price->getWebsiteId()]
+            );
+        }
+    }
+
+    /**
+     * Check website value is unique.
+     *
+     * @param \Magento\Catalog\Api\Data\TierPriceInterface $tierPrice
+     * @param array $prices
+     * @param int $key
+     * @param Result $validationResult
+     * @return void
+     */
+    private function checkUnique(
+        \Magento\Catalog\Api\Data\TierPriceInterface $tierPrice,
+        array $prices,
+        $key,
+        Result $validationResult
+    ) {
+        foreach ($prices as $price) {
+            if (
+                $price->getSku() === $tierPrice->getSku()
+                && strtolower($price->getCustomerGroup()) === strtolower($tierPrice->getCustomerGroup())
+                && $price->getQuantity() == $tierPrice->getQuantity()
+                && (
+                    ($price->getWebsiteId() == $this->allWebsitesValue
+                        || $tierPrice->getWebsiteId() == $this->allWebsitesValue)
+                    && $price->getWebsiteId() != $tierPrice->getWebsiteId()
+                )
+            ) {
+                $validationResult->addFailedItem(
+                    $key,
+                    __(
+                        'We found a duplicate website, tier price, customer group and quantity:'
+                        . ' %fieldName1 = %fieldValue1, %fieldName2 = %fieldValue2, %fieldName3 = %fieldValue3.',
+                        [
+                            'fieldName1' => '%fieldName1',
+                            'fieldValue1' => '%fieldValue1',
+                            'fieldName2' => '%fieldName2',
+                            'fieldValue2' => '%fieldValue2',
+                            'fieldName3' => '%fieldName3',
+                            'fieldValue3' => '%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 \Magento\Catalog\Api\Data\TierPriceInterface $price
+     * @param int $key
+     * @param Result $validationResult
+     * @return void
+     */
+    private function checkGroup(\Magento\Catalog\Api\Data\TierPriceInterface $price, $key, Result $validationResult)
+    {
+        $customerGroup = strtolower($price->getCustomerGroup());
+
+        if ($customerGroup != $this->allGroupsValue && false === $this->retrieveGroupValue($customerGroup)) {
+            $validationResult->addFailedItem(
+                $key,
+                __(
+                    'No such entity with %fieldName = %fieldValue.',
+                    ['fieldName' => '%fieldName', 'fieldValue' => '%fieldValue']
+                ),
+                [
+                    'fieldName' => 'Customer Group',
+                    'fieldValue' => $customerGroup,
+                ]
+            );
+        }
+    }
+
+    /**
+     * Retrieve customer group id by code.
+     *
+     * @param string $code
+     * @return int|bool
+     */
+    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) {
+                return false;
+            }
+
+            $this->customerGroupsByCode[strtolower($item->getCode())] = $item->getId();
+        }
+
+        return $this->customerGroupsByCode[$code];
+    }
+}
diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Price/SpecialPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Price/SpecialPrice.php
new file mode 100644
index 0000000000000000000000000000000000000000..62ada0736ba8821049c9cd45dfccf3d7ba2fb01c
--- /dev/null
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Price/SpecialPrice.php
@@ -0,0 +1,320 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Model\ResourceModel\Product\Price;
+
+/**
+ * Special price resource.
+ */
+class SpecialPrice implements \Magento\Catalog\Api\SpecialPriceInterface
+{
+    /**
+     * Price storage table.
+     *
+     * @var string
+     */
+    private $priceTable = 'catalog_product_entity_decimal';
+
+    /**
+     * Datetime storage table.
+     *
+     * @var string
+     */
+    private $datetimeTable = 'catalog_product_entity_datetime';
+
+    /**
+     * @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;
+
+    /**
+     * Special Price attribute ID.
+     *
+     * @var int
+     */
+    private $priceAttributeId;
+
+    /**
+     * Special price from attribute ID.
+     *
+     * @var int
+     */
+    private $priceFromAttributeId;
+
+    /**
+     * Special price to attribute ID.
+     *
+     * @var int
+     */
+    private $priceToAttributeId;
+
+    /**
+     * Items per operation.
+     *
+     * @var int
+     */
+    private $itemsPerOperation = 500;
+
+    /**
+     * @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
+     */
+    public function __construct(
+        \Magento\Catalog\Model\ResourceModel\Attribute $attributeResource,
+        \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository,
+        \Magento\Catalog\Model\ProductIdLocatorInterface $productIdLocator,
+        \Magento\Framework\EntityManager\MetadataPool $metadataPool
+    ) {
+        $this->attributeResource = $attributeResource;
+        $this->attributeRepository = $attributeRepository;
+        $this->productIdLocator = $productIdLocator;
+        $this->metadataPool = $metadataPool;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function get(array $skus)
+    {
+        $ids = $this->retrieveAffectedIds($skus);
+        $priceTable = $this->attributeResource->getTable($this->priceTable);
+        $dateTimeTable = $this->attributeResource->getTable($this->datetimeTable);
+        $linkField = $this->getEntityLinkField();
+        $select = $this->attributeResource->getConnection()
+            ->select()
+            ->from(
+                $priceTable,
+                [
+                    'value_id',
+                    'store_id',
+                    $this->getEntityLinkField(),
+                    'value',
+                ]
+            )
+            ->joinLeft(
+                $dateTimeTable . ' AS datetime_from',
+                $priceTable . '.' . $linkField . '=' . 'datetime_from.' . $linkField
+                . ' AND datetime_from.attribute_id=' . $this->getPriceFromAttributeId(),
+                'value AS price_from'
+            )
+            ->joinLeft(
+                $dateTimeTable . ' AS datetime_to',
+                $priceTable . '.' . $linkField . '=' . 'datetime_to.' . $linkField
+                . ' AND datetime_to.attribute_id=' . $this->getPriceToAttributeId(),
+                'value AS price_to'
+            )
+            ->where($priceTable . '.' . $linkField . ' IN (?)', $ids)
+            ->where($priceTable . '.attribute_id = ?', $this->getPriceAttributeId());
+
+        return $this->attributeResource->getConnection()->fetchAll($select);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function update(array $prices)
+    {
+        $formattedPrices = [];
+        $formattedDates = [];
+
+        /** @var \Magento\Catalog\Api\Data\SpecialPriceInterface $price */
+        foreach ($prices as $price) {
+            $productIdsBySku = $this->productIdLocator->retrieveProductIdsBySkus([$price->getSku()]);
+            $ids = array_keys($productIdsBySku[$price->getSku()]);
+            foreach ($ids as $id) {
+                $formattedPrices[] = [
+                    'store_id' => $price->getStoreId(),
+                    $this->getEntityLinkField() => $id,
+                    'value' => $price->getPrice(),
+                    'attribute_id' => $this->getPriceAttributeId(),
+                ];
+                if ($price->getPriceFrom()) {
+                    $formattedDates[] = [
+                        'store_id' => $price->getStoreId(),
+                        $this->getEntityLinkField() => $id,
+                        'value' => $price->getPriceFrom(),
+                        'attribute_id' => $this->getPriceFromAttributeId(),
+                    ];
+                }
+                if ($price->getPriceTo()) {
+                    $formattedDates[] = [
+                        'store_id' => $price->getStoreId(),
+                        $this->getEntityLinkField() => $id,
+                        'value' => $price->getPriceTo(),
+                        'attribute_id' => $this->getPriceToAttributeId(),
+                    ];
+                }
+            }
+        }
+        $connection = $this->attributeResource->getConnection();
+        $connection->beginTransaction();
+
+        try {
+            $this->updateItems($formattedPrices, $this->priceTable);
+            $this->updateItems($formattedDates, $this->datetimeTable);
+            $connection->commit();
+        } catch (\Exception $e) {
+            $connection->rollBack();
+            throw new \Magento\Framework\Exception\CouldNotSaveException(
+                __('Could not save Prices.'),
+                $e
+            );
+        }
+
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function delete(array $prices)
+    {
+        $skus = array_unique(
+            array_map(function ($price) {
+                return $price->getSku();
+            }, $prices)
+        );
+        $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->priceTable),
+                    [
+                        'attribute_id = ?' => $this->getPriceAttributeId(),
+                        $this->getEntityLinkField() . ' IN (?)' => $idsBunch
+                    ]
+                );
+            }
+            foreach (array_chunk($ids, $this->itemsPerOperation) as $idsBunch) {
+                $this->attributeResource->getConnection()->delete(
+                    $this->attributeResource->getTable($this->datetimeTable),
+                    [
+                        'attribute_id IN (?)' => [$this->getPriceFromAttributeId(), $this->getPriceToAttributeId()],
+                        $this->getEntityLinkField() . ' IN (?)' => $idsBunch
+                    ]
+                );
+            }
+            $connection->commit();
+        } catch (\Exception $e) {
+            $connection->rollBack();
+            throw new \Magento\Framework\Exception\CouldNotDeleteException(
+                __('Could not delete Prices'),
+                $e
+            );
+        }
+
+        return true;
+    }
+
+    /**
+     * Get link field.
+     *
+     * @return string
+     */
+    public function getEntityLinkField()
+    {
+        return $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\ProductInterface::class)
+            ->getLinkField();
+    }
+
+    /**
+     * Update items in database.
+     *
+     * @param array $items
+     * @param string $table
+     * @return void
+     */
+    private function updateItems(array $items, $table)
+    {
+        foreach (array_chunk($items, $this->itemsPerOperation) as $itemsBunch) {
+            $this->attributeResource->getConnection()->insertOnDuplicate(
+                $this->attributeResource->getTable($table),
+                $itemsBunch,
+                ['value']
+            );
+        }
+    }
+
+    /**
+     * Get special price attribute ID.
+     *
+     * @return int
+     */
+    private function getPriceAttributeId()
+    {
+        if (!$this->priceAttributeId) {
+            $this->priceAttributeId = $this->attributeRepository->get('special_price')->getAttributeId();
+        }
+
+        return $this->priceAttributeId;
+    }
+
+    /**
+     * Get special price from attribute ID.
+     *
+     * @return int
+     */
+    private function getPriceFromAttributeId()
+    {
+        if (!$this->priceFromAttributeId) {
+            $this->priceFromAttributeId = $this->attributeRepository->get('special_from_date')->getAttributeId();
+        }
+
+        return $this->priceFromAttributeId;
+    }
+
+    /**
+     * Get special price to attribute ID.
+     *
+     * @return int
+     */
+    private function getPriceToAttributeId()
+    {
+        if (!$this->priceToAttributeId) {
+            $this->priceToAttributeId = $this->attributeRepository->get('special_to_date')->getAttributeId();
+        }
+
+        return $this->priceToAttributeId;
+    }
+
+    /**
+     * Retrieve IDs of products, that were affected during price update.
+     *
+     * @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);
+    }
+}
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
index 61ea55228df9fc676a4634d61b72088b44ed855d..fe268803caba47816754a370f5d53b46d3d9f465 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/BasePriceStorageTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/BasePriceStorageTest.php
@@ -8,6 +8,8 @@ namespace Magento\Catalog\Test\Unit\Model\Product\Price;
 
 /**
  * Class BasePriceStorageTest.
+ *
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  */
 class BasePriceStorageTest extends \PHPUnit_Framework_TestCase
 {
@@ -51,6 +53,16 @@ class BasePriceStorageTest extends \PHPUnit_Framework_TestCase
      */
     private $product;
 
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\InvalidSkuChecker|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $invalidSkuChecker;
+
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\Validation\Result|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $validationResult;
+
     /**
      * @var \Magento\Catalog\Model\Product\Price\BasePriceStorage
      */
@@ -60,6 +72,7 @@ class BasePriceStorageTest extends \PHPUnit_Framework_TestCase
      * Set up.
      *
      * @return void
+     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
      */
     protected function setUp()
     {
@@ -129,7 +142,24 @@ class BasePriceStorageTest extends \PHPUnit_Framework_TestCase
             true,
             ['getPriceType']
         );
-
+        $this->invalidSkuChecker = $this->getMockForAbstractClass(
+            \Magento\Catalog\Model\Product\Price\InvalidSkuChecker::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            ['retrieveInvalidSkuList']
+        );
+        $this->validationResult = $this->getMockForAbstractClass(
+            \Magento\Catalog\Model\Product\Price\Validation\Result::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            ['getFailedRowIds', 'getFailedItems']
+        );
         $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
         $this->model = $objectManager->getObject(
             \Magento\Catalog\Model\Product\Price\BasePriceStorage::class,
@@ -139,6 +169,8 @@ class BasePriceStorageTest extends \PHPUnit_Framework_TestCase
                 'productIdLocator' => $this->productIdLocator,
                 'storeRepository' => $this->storeRepository,
                 'productRepository' => $this->productRepository,
+                'invalidSkuChecker' => $this->invalidSkuChecker,
+                'validationResult' => $this->validationResult,
                 'allowedProductTypes' => ['simple', 'virtual', 'bundle', 'downloadable'],
             ]
         );
@@ -152,16 +184,6 @@ class BasePriceStorageTest extends \PHPUnit_Framework_TestCase
     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,
@@ -174,12 +196,6 @@ class BasePriceStorageTest extends \PHPUnit_Framework_TestCase
                 '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')
@@ -210,6 +226,7 @@ class BasePriceStorageTest extends \PHPUnit_Framework_TestCase
             ->method('setStoreId')
             ->withConsecutive([1], [1])
             ->willReturnSelf();
+        $this->invalidSkuChecker->expects($this->once())->method('retrieveInvalidSkuList')->willReturn([]);
         $this->model->get($skus);
     }
 
@@ -222,20 +239,7 @@ class BasePriceStorageTest extends \PHPUnit_Framework_TestCase
     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->invalidSkuChecker->expects($this->once())->method('retrieveInvalidSkuList')->willReturn($skus);
         $this->model->get($skus);
     }
 
@@ -256,16 +260,18 @@ class BasePriceStorageTest extends \PHPUnit_Framework_TestCase
         $idsBySku = [
             'sku_1' =>
                 [
-                    1 => \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE
+                    1 => [
+                        $this->basePriceInterface
+                    ]
                 ]
         ];
-        $this->basePriceInterface->expects($this->exactly(4))->method('getSku')->willReturn($sku);
+        $this->basePriceInterface->expects($this->exactly(5))->method('getSku')->willReturn($sku);
+        $this->invalidSkuChecker->expects($this->once())->method('retrieveInvalidSkuList')->willReturn([]);
+        $this->validationResult->expects($this->once())->method('getFailedRowIds')->willReturn([]);
         $this->productIdLocator
-            ->expects($this->exactly(2))
+            ->expects($this->exactly(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->basePriceInterface->expects($this->exactly(2))->method('getStoreId')->willReturn(1);
         $this->pricePersistence->expects($this->atLeastOnce())->method('getEntityLinkField')->willReturn('row_id');
@@ -283,44 +289,71 @@ class BasePriceStorageTest extends \PHPUnit_Framework_TestCase
             ]
         ];
         $this->pricePersistence->expects($this->once())->method('update')->with($formattedPrices);
-        $this->assertTrue($this->model->update([$this->basePriceInterface]));
+        $this->validationResult->expects($this->any())->method('getFailedItems')->willReturn([]);
+        $this->assertEquals([], $this->model->update([1 => $this->basePriceInterface]));
     }
 
     /**
      * Test update method without SKU.
      *
-     * @expectedException \Magento\Framework\Exception\LocalizedException
-     * @expectedExceptionMessage Invalid attribute sku: .
+     * @return void
      */
     public function testUpdateWithoutSku()
     {
-        $this->basePriceInterface->expects($this->exactly(2))->method('getSku')->willReturn(null);
-        $this->model->update([$this->basePriceInterface]);
+        $this->basePriceInterface->expects($this->exactly(3))->method('getSku')->willReturn(null);
+        $this->validationResult->expects($this->once())->method('getFailedRowIds')->willReturn([0 => 0]);
+        $this->pricePersistenceFactory
+            ->expects($this->once())
+            ->method('create')
+            ->with(['attributeCode' => 'price'])
+            ->willReturn($this->pricePersistence);
+        $priceUpdateResult = $this->getMockForAbstractClass(
+            \Magento\Catalog\Api\Data\PriceUpdateResultInterface::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            []
+        );
+
+        $this->validationResult->expects($this->any())->method('getFailedItems')->willReturn([$priceUpdateResult]);
+        $this->assertEquals(
+            [$priceUpdateResult],
+            $this->model->update([$this->basePriceInterface])
+        );
     }
 
     /**
      * Test update method with negative price.
      *
-     * @expectedException \Magento\Framework\Exception\LocalizedException
-     * @expectedExceptionMessage Invalid attribute Price: -15.
+     * @return void
      */
     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->invalidSkuChecker->expects($this->once())->method('retrieveInvalidSkuList')->willReturn([]);
+        $this->validationResult->expects($this->once())->method('getFailedRowIds')->willReturn([0 => 0]);
+        $this->basePriceInterface->expects($this->exactly(3))->method('getSku')->willReturn($sku);
         $this->basePriceInterface->expects($this->exactly(3))->method('getPrice')->willReturn(-15);
-        $this->model->update([$this->basePriceInterface]);
+        $priceUpdateResult = $this->getMockForAbstractClass(
+            \Magento\Catalog\Api\Data\PriceUpdateResultInterface::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            []
+        );
+        $this->pricePersistenceFactory
+            ->expects($this->once())
+            ->method('create')
+            ->with(['attributeCode' => 'price'])
+            ->willReturn($this->pricePersistence);
+        $this->validationResult->expects($this->any())->method('getFailedItems')->willReturn([$priceUpdateResult]);
+        $this->assertEquals(
+            [$priceUpdateResult],
+            $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
index 16a7a2407c3deffe82efe084400a6ede1a172009..78f6c6540b6cb3487711c2d70949b2039ead5b14 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/CostStorageTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/CostStorageTest.php
@@ -41,6 +41,16 @@ class CostStorageTest extends \PHPUnit_Framework_TestCase
      */
     private $storeRepository;
 
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\InvalidSkuChecker|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $invalidSkuChecker;
+
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\Validation\Result|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $validationResult;
+
     /**
      * @var \Magento\Catalog\Model\Product\Price\CostStorage
      */
@@ -101,6 +111,12 @@ class CostStorageTest extends \PHPUnit_Framework_TestCase
             true,
             ['getById']
         );
+        $this->validationResult = $this->getMockBuilder(\Magento\Catalog\Model\Product\Price\Validation\Result::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->invalidSkuChecker = $this->getMockBuilder(\Magento\Catalog\Model\Product\Price\InvalidSkuChecker::class)
+            ->disableOriginalConstructor()
+            ->getMock();
 
         $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
         $this->model = $objectManager->getObject(
@@ -110,6 +126,8 @@ class CostStorageTest extends \PHPUnit_Framework_TestCase
                 'costInterfaceFactory' => $this->costInterfaceFactory,
                 'productIdLocator' => $this->productIdLocator,
                 'storeRepository' => $this->storeRepository,
+                'validationResult' => $this->validationResult,
+                'invalidSkuChecker' => $this->invalidSkuChecker,
                 'allowedProductTypes' => ['simple', 'virtual', 'downloadable'],
             ]
         );
@@ -123,16 +141,6 @@ class CostStorageTest extends \PHPUnit_Framework_TestCase
     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,
@@ -145,10 +153,9 @@ class CostStorageTest extends \PHPUnit_Framework_TestCase
                 'store_id' => 1
             ]
         ];
-        $this->productIdLocator
-            ->expects($this->once())
-            ->method('retrieveProductIdsBySkus')->with($skus)
-            ->willReturn($idsBySku);
+        $this->invalidSkuChecker
+            ->expects($this->atLeastOnce())
+            ->method('isSkuListValid');
         $this->pricePersistenceFactory
             ->expects($this->once())
             ->method('create')
@@ -182,32 +189,6 @@ class CostStorageTest extends \PHPUnit_Framework_TestCase
         $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.
      *
@@ -228,12 +209,16 @@ class CostStorageTest extends \PHPUnit_Framework_TestCase
                     1 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL
                 ]
         ];
-        $this->costInterface->expects($this->exactly(4))->method('getSku')->willReturn($sku);
+        $this->costInterface->expects($this->exactly(5))->method('getSku')->willReturn($sku);
         $this->productIdLocator
-            ->expects($this->exactly(2))
+            ->expects($this->exactly(1))
             ->method('retrieveProductIdsBySkus')->with([$sku])
             ->willReturn($idsBySku);
         $this->costInterface->expects($this->exactly(3))->method('getCost')->willReturn(15);
+        $this->invalidSkuChecker
+            ->expects($this->exactly(1))
+            ->method('retrieveInvalidSkuList')
+            ->willReturn([]);
         $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);
@@ -242,6 +227,10 @@ class CostStorageTest extends \PHPUnit_Framework_TestCase
             ->method('create')
             ->with(['attributeCode' => 'cost'])
             ->willReturn($this->pricePersistence);
+        $this->validationResult
+            ->expects($this->exactly(1))
+            ->method('getFailedRowIds')
+            ->willReturn([]);
         $formattedPrices = [
             [
                 'store_id' => 1,
@@ -250,42 +239,38 @@ class CostStorageTest extends \PHPUnit_Framework_TestCase
             ]
         ];
         $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]);
+        $this->validationResult
+            ->expects($this->exactly(1))
+            ->method('getFailedItems')
+            ->willReturn([]);
+        $this->assertEmpty($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('getSku')->willReturn($sku);
+        $this->invalidSkuChecker
+            ->expects($this->exactly(1))
+            ->method('retrieveInvalidSkuList')
+            ->willReturn([]);
+        $this->validationResult
+            ->expects($this->exactly(1))
+            ->method('getFailedRowIds')
+            ->willReturn([0]);
+        $this->pricePersistenceFactory
+            ->expects($this->once())
+            ->method('create')
+            ->with(['attributeCode' => 'cost'])
+            ->willReturn($this->pricePersistence);
+        $this->pricePersistence->expects($this->atLeastOnce())->method('update');
         $this->costInterface->expects($this->exactly(3))->method('getCost')->willReturn(-15);
+        $this->validationResult
+            ->expects($this->atLeastOnce())
+            ->method('getFailedItems');
         $this->model->update([$this->costInterface]);
     }
 
@@ -297,26 +282,16 @@ class CostStorageTest extends \PHPUnit_Framework_TestCase
     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->invalidSkuChecker
+            ->expects($this->exactly(1))
+            ->method('isSkuListValid')
+            ->willReturn(true);
         $this->model->delete($skus);
     }
 }
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/InvalidSkuCheckerTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/InvalidSkuCheckerTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..30131e553f54433dc16375d980dae6210cee5380
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/InvalidSkuCheckerTest.php
@@ -0,0 +1,121 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Test\Unit\Model\Product\Price;
+
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+
+/**
+ * Test for model Product\Price\InvalidSkuChecker.
+ */
+class InvalidSkuCheckerTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var ObjectManagerHelper
+     */
+    private $objectManagerHelper;
+
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\InvalidSkuChecker
+     */
+    private $invalidSkuChecker;
+
+    /**
+     * @var \Magento\Catalog\Model\ProductIdLocatorInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $productIdLocator;
+
+    /**
+     * @var \Magento\Catalog\Api\ProductRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $productRepository;
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function setUp()
+    {
+        $this->productIdLocator = $this->getMockBuilder(\Magento\Catalog\Model\ProductIdLocatorInterface::class)
+            ->setMethods(['retrieveProductIdsBySkus'])
+            ->disableOriginalConstructor()->getMockForAbstractClass();
+
+        $this->productRepository = $this->getMockBuilder(\Magento\Catalog\Api\ProductRepositoryInterface::class)
+            ->setMethods(['get'])
+            ->disableOriginalConstructor()->getMockForAbstractClass();
+
+        $this->objectManagerHelper = new ObjectManagerHelper($this);
+        $this->invalidSkuChecker = $this->objectManagerHelper->getObject(
+            \Magento\Catalog\Model\Product\Price\InvalidSkuChecker::class,
+            [
+                'productIdLocator' => $this->productIdLocator,
+                'productRepository' => $this->productRepository
+            ]
+        );
+    }
+
+    /**
+     * Prepare retrieveInvalidSkuList().
+     *
+     * @param string $productType
+     * @param string $productSku
+     * @return void
+     */
+    private function prepareRetrieveInvalidSkuListMethod($productType, $productSku)
+    {
+        $idsBySku = [$productSku => [235235235 => $productType]];
+        $this->productIdLocator->expects($this->atLeastOnce())->method('retrieveProductIdsBySkus')
+            ->willReturn($idsBySku);
+
+        $product = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterface::class)
+            ->setMethods(['getPriceType'])
+            ->disableOriginalConstructor()->getMockForAbstractClass();
+        $productPriceType = 0;
+        $product->expects($this->atLeastOnce())->method('getPriceType')->willReturn($productPriceType);
+
+        $this->productRepository->expects($this->atLeastOnce())->method('get')->willReturn($product);
+    }
+
+    /**
+     * Test for retrieveInvalidSkuList().
+     *
+     * @return void
+     */
+    public function testRetrieveInvalidSkuList()
+    {
+        $productSku = 'LKJKJ2233636';
+        $productType = \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE;
+        $methodParamSku = 'SDFSDF3242355';
+        $skus = [$methodParamSku];
+        $allowedProductTypes = [$productType];
+        $allowedPriceTypeValue = true;
+
+        $this->prepareRetrieveInvalidSkuListMethod($productType, $productSku);
+
+        $expects = [$methodParamSku, $productSku];
+        $result = $this->invalidSkuChecker->retrieveInvalidSkuList($skus, $allowedProductTypes, $allowedPriceTypeValue);
+        $this->assertEquals($expects, $result);
+    }
+
+    /**
+     * Test for isSkuListValid() with Exception.
+     *
+     * @expectedException \Magento\Framework\Exception\NoSuchEntityException
+     * @return void
+     */
+    public function testIsSkuListValidWithException()
+    {
+        $productSku = 'LKJKJ2233636';
+        $productType = \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE;
+        $methodParamSku = 'SDFSDF3242355';
+        $skus = [$methodParamSku];
+        $allowedProductTypes = [$productType];
+        $allowedPriceTypeValue = true;
+
+        $this->prepareRetrieveInvalidSkuListMethod($productType, $productSku);
+
+        $this->invalidSkuChecker->isSkuListValid($skus, $allowedProductTypes, $allowedPriceTypeValue);
+    }
+}
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/SpecialPriceStorageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/SpecialPriceStorageTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..3a8368b1c096aae4b288610f335e4c20756623ed
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/SpecialPriceStorageTest.php
@@ -0,0 +1,294 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Test\Unit\Model\Product\Price;
+
+/**
+ * Test for SpecialPriceStorage model.
+ */
+class SpecialPriceStorageTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\Catalog\Api\SpecialPriceInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $specialPriceResource;
+
+    /**
+     * @var \Magento\Catalog\Api\Data\SpecialPriceInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $specialPriceFactory;
+
+    /**
+     * @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\InvalidSkuChecker|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $invalidSkuChecker;
+
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\Validation\Result|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $validationResult;
+
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\SpecialPriceStorage
+     */
+    private $model;
+
+    /**
+     * Set up.
+     *
+     * @return void
+     */
+    protected function setUp()
+    {
+        $this->specialPriceResource = $this->getMockBuilder(\Magento\Catalog\Api\SpecialPriceInterface::class)
+            ->disableOriginalConstructor()->setMethods(['get', 'update', 'delete', 'getEntityLinkField'])->getMock();
+        $this->productIdLocator = $this->getMockBuilder(\Magento\Catalog\Model\ProductIdLocatorInterface::class)
+            ->disableOriginalConstructor()->getMock();
+        $this->storeRepository = $this->getMockBuilder(\Magento\Store\Api\StoreRepositoryInterface::class)
+            ->disableOriginalConstructor()->getMock();
+        $this->invalidSkuChecker = $this->getMockBuilder(\Magento\Catalog\Model\Product\Price\InvalidSkuChecker::class)
+            ->disableOriginalConstructor()->getMock();
+        $this->validationResult = $this->getMockBuilder(\Magento\Catalog\Model\Product\Price\Validation\Result::class)
+            ->disableOriginalConstructor()->getMock();
+        $this->specialPriceFactory = $this->getMockBuilder(
+            \Magento\Catalog\Api\Data\SpecialPriceInterfaceFactory::class
+        )->disableOriginalConstructor()->setMethods(['create'])->getMock();
+
+        $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+        $this->model = $objectManager->getObject(
+            \Magento\Catalog\Model\Product\Price\SpecialPriceStorage::class,
+            [
+                'specialPriceResource' => $this->specialPriceResource,
+                'specialPriceFactory' => $this->specialPriceFactory,
+                'productIdLocator' => $this->productIdLocator,
+                'storeRepository' => $this->storeRepository,
+                'invalidSkuChecker' => $this->invalidSkuChecker,
+                'validationResult' => $this->validationResult,
+            ]
+        );
+    }
+
+    /**
+     * Test get method.
+     *
+     * @return void
+     */
+    public function testGet()
+    {
+        $skus = ['sku_1', 'sku_2'];
+        $rawPrices = [
+            [
+                'entity_id' => 1,
+                'value' => 15,
+                'store_id' => 1,
+                'sku' => 'sku_1',
+                'price_from' => '2016-12-20 01:02:03',
+                'price_to' => '2016-12-21 01:02:03',
+            ],
+            [
+                'entity_id' => 2,
+                'value' => 15,
+                'store_id' => 1,
+                'price_from' => '2016-12-20 01:02:03',
+                'price_to' => '2016-12-21 01:02:03',
+            ],
+            [
+                'entity_id' => 3,
+                'value' => 15,
+                'store_id' => 1,
+                'price_from' => '2016-12-20 01:02:03',
+                'price_to' => '2016-12-21 01:02:03',
+            ],
+        ];
+        $this->specialPriceResource->expects($this->once())->method('get')->willReturn($rawPrices);
+        $this->specialPriceResource->expects($this->atLeastOnce())
+            ->method('getEntityLinkField')->willReturn('entity_id');
+
+        $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\SpecialPriceInterface::class)
+            ->disableOriginalConstructor()->getMock();
+        $price->expects($this->exactly(3))->method('setPrice');
+        $this->specialPriceFactory->expects($this->atLeastOnce())->method('create')->willReturn($price);
+        $this->productIdLocator->expects($this->atLeastOnce())->method('retrieveProductIdsBySkus')->willReturn(
+            [
+                'sku_2' => [2 => 'prod']
+            ]
+        );
+        $this->model->get($skus);
+    }
+
+    /**
+     * Test update method.
+     *
+     * @return void
+     */
+    public function testUpdate()
+    {
+        $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\SpecialPriceInterface::class)
+            ->disableOriginalConstructor()->getMock();
+
+        $prices = [1 => $price];
+        $price->expects($this->atLeastOnce())->method('getSku')->willReturn('sku_1');
+        $price->expects($this->atLeastOnce())->method('getPrice')->willReturn(15);
+        $price->expects($this->atLeastOnce())->method('getStoreId')->willReturn(1);
+        $price->expects($this->atLeastOnce())->method('getPriceFrom')->willReturn('2016-12-20 01:02:03');
+        $price->expects($this->atLeastOnce())->method('getPriceTo')->willReturn('2016-12-21 01:02:03');
+
+        $this->invalidSkuChecker->expects($this->once())->method('retrieveInvalidSkuList')->willReturn([]);
+        $this->storeRepository->expects($this->once())->method('getById');
+        $this->validationResult->expects($this->never())->method('addFailedItem');
+        $this->validationResult->expects($this->atLeastOnce())->method('getFailedRowIds')->willReturn([]);
+
+        $this->specialPriceResource->expects($this->once())->method('update')->with($prices);
+
+        $this->model->update($prices);
+    }
+
+    /**
+     * Test update method with invalid sku.
+     *
+     * @return void
+     */
+    public function testUpdateWithInvalidSku()
+    {
+        $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\SpecialPriceInterface::class)
+            ->disableOriginalConstructor()->getMock();
+        $prices = [1 => $price];
+
+        $price->expects($this->atLeastOnce())->method('getSku')->willReturn('sku_1');
+        $price->expects($this->atLeastOnce())->method('getPrice')->willReturn(15);
+        $price->expects($this->atLeastOnce())->method('getStoreId')->willReturn(1);
+        $price->expects($this->atLeastOnce())->method('getPriceFrom')->willReturn('2016-12-20 01:02:03');
+        $price->expects($this->atLeastOnce())->method('getPriceTo')->willReturn('2016-12-21 01:02:03');
+
+        $this->invalidSkuChecker->expects($this->once())->method('retrieveInvalidSkuList')->willReturn(['sku_1']);
+        $this->storeRepository->expects($this->once())->method('getById');
+        $this->validationResult->expects($this->once())->method('addFailedItem')->with(1);
+        $this->validationResult->expects($this->atLeastOnce())->method('getFailedRowIds')->willReturn([1]);
+
+        $this->specialPriceResource->expects($this->once())->method('update')->with([]);
+
+        $this->model->update($prices);
+    }
+
+    /**
+     * Test update method with price = null.
+     *
+     * @return void
+     */
+    public function testUpdateWithoutPrice()
+    {
+        $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\SpecialPriceInterface::class)
+            ->disableOriginalConstructor()->getMock();
+        $prices = [1 => $price];
+
+        $price->expects($this->atLeastOnce())->method('getSku')->willReturn('sku_1');
+        $price->expects($this->atLeastOnce())->method('getPrice')->willReturn(null);
+        $price->expects($this->atLeastOnce())->method('getStoreId')->willReturn(1);
+        $price->expects($this->atLeastOnce())->method('getPriceFrom')->willReturn('2016-12-20 01:02:03');
+        $price->expects($this->atLeastOnce())->method('getPriceTo')->willReturn('2016-12-21 01:02:03');
+
+        $this->invalidSkuChecker->expects($this->once())->method('retrieveInvalidSkuList')->willReturn([]);
+        $this->storeRepository->expects($this->once())->method('getById');
+        $this->validationResult->expects($this->once())->method('addFailedItem')->with(1);
+        $this->validationResult->expects($this->atLeastOnce())->method('getFailedRowIds')->willReturn([1]);
+
+        $this->specialPriceResource->expects($this->once())->method('update')->with([]);
+
+        $this->model->update($prices);
+    }
+
+    /**
+     * Test update method with price = null.
+     *
+     * @return void
+     */
+    public function testUpdateWithException()
+    {
+        $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\SpecialPriceInterface::class)
+            ->disableOriginalConstructor()->getMock();
+        $prices = [1 => $price];
+
+        $price->expects($this->atLeastOnce())->method('getSku')->willReturn('sku_1');
+        $price->expects($this->atLeastOnce())->method('getPrice')->willReturn(15);
+        $price->expects($this->atLeastOnce())->method('getStoreId')->willReturn(1);
+        $price->expects($this->atLeastOnce())->method('getPriceFrom')->willReturn('2016-12-20 01:02:03');
+        $price->expects($this->atLeastOnce())->method('getPriceTo')->willReturn('2016-12-21 01:02:03');
+
+        $this->invalidSkuChecker->expects($this->once())->method('retrieveInvalidSkuList')->willReturn([]);
+        $this->storeRepository->expects($this->once())->method('getById')
+            ->willThrowException(new \Magento\Framework\Exception\NoSuchEntityException());
+        $this->validationResult->expects($this->once())->method('addFailedItem')->with(1);
+        $this->validationResult->expects($this->atLeastOnce())->method('getFailedRowIds')->willReturn([1]);
+
+        $this->specialPriceResource->expects($this->once())->method('update')->with([]);
+
+        $this->model->update($prices);
+    }
+
+    /**
+     * Test update method with incorrect price_from field.
+     *
+     * @return void
+     */
+    public function testUpdateWithIncorrectPriceFrom()
+    {
+        $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\SpecialPriceInterface::class)
+            ->disableOriginalConstructor()->getMock();
+        $prices = [1 => $price];
+
+        $price->expects($this->atLeastOnce())->method('getSku')->willReturn('sku_1');
+        $price->expects($this->atLeastOnce())->method('getPrice')->willReturn(15);
+        $price->expects($this->atLeastOnce())->method('getStoreId')->willReturn(1);
+        $price->expects($this->atLeastOnce())->method('getPriceFrom')->willReturn('incorrect');
+        $price->expects($this->atLeastOnce())->method('getPriceTo')->willReturn('2016-12-21 01:02:03');
+
+        $this->invalidSkuChecker->expects($this->once())->method('retrieveInvalidSkuList')->willReturn([]);
+        $this->storeRepository->expects($this->once())->method('getById');
+        $this->validationResult->expects($this->once())->method('addFailedItem')->with(1);
+        $this->validationResult->expects($this->atLeastOnce())->method('getFailedRowIds')->willReturn([1]);
+
+        $this->specialPriceResource->expects($this->once())->method('update')->with([]);
+
+        $this->model->update($prices);
+    }
+
+    /**
+     * Test update method with incorrect price_to field.
+     *
+     * @return void
+     */
+    public function testUpdateWithIncorrectPriceTo()
+    {
+        $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\SpecialPriceInterface::class)
+            ->disableOriginalConstructor()->getMock();
+        $prices = [1 => $price];
+
+        $price->expects($this->atLeastOnce())->method('getSku')->willReturn('sku_1');
+        $price->expects($this->atLeastOnce())->method('getPrice')->willReturn(15);
+        $price->expects($this->atLeastOnce())->method('getStoreId')->willReturn(1);
+        $price->expects($this->atLeastOnce())->method('getPriceFrom')->willReturn('2016-12-21 01:02:03');
+        $price->expects($this->atLeastOnce())->method('getPriceTo')->willReturn('incorrect');
+
+        $this->invalidSkuChecker->expects($this->once())->method('retrieveInvalidSkuList')->willReturn([]);
+        $this->storeRepository->expects($this->once())->method('getById');
+        $this->validationResult->expects($this->once())->method('addFailedItem')->with(1);
+        $this->validationResult->expects($this->atLeastOnce())->method('getFailedRowIds')->willReturn([1]);
+
+        $this->specialPriceResource->expects($this->once())->method('update')->with([]);
+
+        $this->model->update($prices);
+    }
+}
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
index b440ee21f41e5e4a7601597fb4a8043f4a09c6ab..398b340da08cd10e22c69a3ef63553559f2f7af4 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceStorageTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceStorageTest.php
@@ -17,7 +17,7 @@ class TierPriceStorageTest extends \PHPUnit_Framework_TestCase
     private $tierPricePersistence;
 
     /**
-     * @var \Magento\Catalog\Model\Product\Price\TierPriceValidator|\PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\Catalog\Model\Product\Price\Validation\TierPriceValidator|\PHPUnit_Framework_MockObject_MockObject
      */
     private $tierPriceValidator;
 
@@ -51,6 +51,11 @@ class TierPriceStorageTest extends \PHPUnit_Framework_TestCase
      */
     private $tierPriceStorage;
 
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\InvalidSkuChecker|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $invalidSkuChecker;
+
     /**
      * {@inheritdoc}
      */
@@ -67,7 +72,7 @@ class TierPriceStorageTest extends \PHPUnit_Framework_TestCase
             ->method('getEntityLinkField')
             ->willReturn('row_id');
         $this->tierPriceValidator = $this->getMock(
-            \Magento\Catalog\Model\Product\Price\TierPriceValidator::class,
+            \Magento\Catalog\Model\Product\Price\Validation\TierPriceValidator::class,
             [],
             [],
             '',
@@ -108,6 +113,10 @@ class TierPriceStorageTest extends \PHPUnit_Framework_TestCase
             '',
             false
         );
+        $this->invalidSkuChecker = $this->getMockBuilder(\Magento\Catalog\Model\Product\Price\InvalidSkuChecker::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
         $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
         $this->tierPriceStorage = $objectManager->getObject(
             \Magento\Catalog\Model\Product\Price\TierPriceStorage::class,
@@ -118,6 +127,7 @@ class TierPriceStorageTest extends \PHPUnit_Framework_TestCase
                 'priceIndexer' => $this->priceIndexer,
                 'productIdLocator' => $this->productIdLocator,
                 'config' => $this->config,
+                'invalidSkuChecker' => $this->invalidSkuChecker,
                 'typeList' => $this->typeList,
             ]
         );
@@ -174,10 +184,18 @@ class TierPriceStorageTest extends \PHPUnit_Framework_TestCase
      */
     public function testUpdate()
     {
+        $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\TierPriceInterface::class)->getMockForAbstractClass();
+        $result = $this->getMockBuilder(\Magento\Catalog\Model\Product\Price\Validation\Result::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $result->expects($this->atLeastOnce())->method('getFailedRowIds')->willReturn([]);
         $this->productIdLocator->expects($this->atLeastOnce())
             ->method('retrieveProductIdsBySkus')
-            ->willReturn(['bundle' => ['2' => 'bundle']]);
-        $this->tierPriceValidator->expects($this->atLeastOnce())->method('validatePrices')->willReturn(true);
+            ->willReturn(['simple' => ['2' => 'simple'], 'virtual' => ['3' => 'virtual']]);
+        $this->tierPriceValidator
+            ->expects($this->atLeastOnce())
+            ->method('retrieveValidationResult')
+            ->willReturn($result);
         $this->tierPriceFactory->expects($this->atLeastOnce())->method('createSkeleton')->willReturn(
             [
                 'row_id' => 2,
@@ -209,9 +227,8 @@ class TierPriceStorageTest extends \PHPUnit_Framework_TestCase
         $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]));
+        $price->method('getSku')->willReturn('simple');
+        $this->assertEmpty($this->tierPriceStorage->update([$price]));
     }
 
     /**
@@ -220,10 +237,20 @@ class TierPriceStorageTest extends \PHPUnit_Framework_TestCase
      */
     public function testReplace()
     {
-        $this->tierPriceValidator->expects($this->atLeastOnce())->method('validatePrices');
+        $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\TierPriceInterface::class)->getMockForAbstractClass();
+        $price->method('getSku')->willReturn('virtual');
+        $result = $this->getMockBuilder(\Magento\Catalog\Model\Product\Price\Validation\Result::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $result->expects($this->atLeastOnce())->method('getFailedRowIds')->willReturn([]);
         $this->productIdLocator->expects($this->atLeastOnce())
             ->method('retrieveProductIdsBySkus')
-            ->willReturn(['virtual' => ['2' => 'virtual']]);
+            ->willReturn(['simple' => ['2' => 'simple'], 'virtual' => ['3' => 'virtual']]);
+
+        $this->tierPriceValidator
+            ->expects($this->atLeastOnce())
+            ->method('retrieveValidationResult')
+            ->willReturn($result);
         $this->tierPriceFactory->expects($this->atLeastOnce())->method('createSkeleton')->willReturn(
             [
                 'row_id' => 3,
@@ -237,11 +264,9 @@ class TierPriceStorageTest extends \PHPUnit_Framework_TestCase
         );
         $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]));
+        $this->assertEmpty($this->tierPriceStorage->replace([$price]));
     }
 
     /**
@@ -250,7 +275,15 @@ class TierPriceStorageTest extends \PHPUnit_Framework_TestCase
      */
     public function testDelete()
     {
-        $this->tierPriceValidator->expects($this->atLeastOnce())->method('validatePrices');
+        $price = $this->getMockBuilder(\Magento\Catalog\Api\Data\TierPriceInterface::class)->getMockForAbstractClass();
+        $price->method('getSku')->willReturn('simple');
+        $result = $this->getMockBuilder(\Magento\Catalog\Model\Product\Price\Validation\Result::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $result->expects($this->atLeastOnce())->method('getFailedRowIds')->willReturn([]);
+        $this->tierPriceValidator->expects($this->atLeastOnce())
+            ->method('retrieveValidationResult')
+            ->willReturn($result);
         $this->productIdLocator->expects($this->atLeastOnce())
             ->method('retrieveProductIdsBySkus')
             ->willReturn(['simple' => ['2' => 'simple']]);
@@ -285,8 +318,6 @@ class TierPriceStorageTest extends \PHPUnit_Framework_TestCase
         $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]));
+        $this->assertEmpty($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
deleted file mode 100644
index 8373098593634d27bf21534dd510bd592d3238ca..0000000000000000000000000000000000000000
--- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/TierPriceValidatorTest.php
+++ /dev/null
@@ -1,471 +0,0 @@
-<?php
-/**
- * Copyright © 2013-2017 Magento, Inc. 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/Product/Price/Validation/TierPriceValidatorTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/Validation/TierPriceValidatorTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..dfa75595ff9954c54d009495a55ae7b8d66e3486
--- /dev/null
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Price/Validation/TierPriceValidatorTest.php
@@ -0,0 +1,333 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Catalog\Test\Unit\Model\Product\Price\Validation;
+
+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\InvalidSkuChecker|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $invalidSkuChecker;
+
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\Validation\Result|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $validationResult;
+
+    /**
+     * @var \Magento\Catalog\Model\Product\Price\Validation\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->filterBuilder->method('setField')->willReturnSelf();
+        $this->filterBuilder->method('setValue')->willReturnSelf();
+        $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']
+        );
+        $this->validationResult = $this->getMockBuilder(\Magento\Catalog\Model\Product\Price\Validation\Result::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->invalidSkuChecker = $this->getMockBuilder(\Magento\Catalog\Model\Product\Price\InvalidSkuChecker::class)
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+        $this->model = $objectManager->getObject(
+            \Magento\Catalog\Model\Product\Price\Validation\TierPriceValidator::class,
+            [
+                'productIdLocator' => $this->productIdLocator,
+                'searchCriteriaBuilder' => $this->searchCriteriaBuilder,
+                'filterBuilder' => $this->filterBuilder,
+                'customerGroupRepository' => $this->customerGroupRepository,
+                'websiteRepository' => $this->websiteRepository,
+                'tierPricePersistence' => $this->tierPricePersistence,
+                'validationResult' => $this->validationResult,
+                'invalidSkuChecker' => $this->invalidSkuChecker,
+                'allowedProductTypes' => ['simple', 'virtual', 'bundle', 'downloadable'],
+            ]
+        );
+    }
+
+    /**
+     * Test retrieveValidPrices method.
+     *
+     * @return void
+     */
+    public function testRetrieveValidPrices()
+    {
+        $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->productIdLocator->expects($this->once())->method('retrieveProductIdsBySkus')->willReturn($idsBySku);
+        $productPrice = 15;
+        $this->tierPriceInterface->expects($this->exactly(10))->method('getSku')->willReturn($sku);
+        $this->invalidSkuChecker->expects($this->once())->method('retrieveInvalidSkuList')->willReturn([]);
+        $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->retrieveValidationResult([$this->tierPriceInterface], []);
+    }
+
+    /**
+     * Test retrieveValidPrices method with downloadable product.
+     */
+    public function testRetrieveValidPricesWithDownloadableProduct()
+    {
+        $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')->willReturn($idsBySku);
+        $this->tierPriceInterface->expects($this->exactly(10))->method('getSku')->willReturn('sku_1');
+        $this->invalidSkuChecker->expects($this->once())->method('retrieveInvalidSkuList')->willReturn([]);
+        $productPrice = 15;
+        $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->validationResult
+            ->expects($this->never())
+            ->method('addFailedItem');
+        $this->model->retrieveValidationResult([$this->tierPriceInterface], []);
+    }
+
+    /**
+     * Test method call with invalid values.
+     */
+    public function testRetrieveValidPricesWithInvalidCall()
+    {
+        $idsBySku = [
+            'sku_1' => [1 => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE],
+            'sku_2' => [2 => \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL],
+            'invalid' => [3 => \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE],
+        ];
+
+        $this->tierPriceInterface->expects($this->exactly(10))->method('getSku')->willReturn('sku_1');
+        $this->invalidSkuChecker->expects($this->once())->method('retrieveInvalidSkuList')->willReturn(['invalid']);
+        $this->productIdLocator->expects($this->once())->method('retrieveProductIdsBySkus')->willReturn($idsBySku);
+        $this->validationResult
+            ->expects($this->exactly(5))
+            ->method('addFailedItem');
+        $this->tierPriceInterface->expects($this->exactly(3))->method('getPrice')->willReturn('-90');
+        $this->tierPriceInterface->expects($this->exactly(2))->method('getPriceType')->willReturn('unknown');
+        $this->tierPriceInterface->expects($this->exactly(4))->method('getQuantity')->willReturn('-90');
+        $this->websiteRepository->method('getById')
+            ->willThrowException(new \Magento\Framework\Exception\NoSuchEntityException());
+        $searchCriteria = $this->getMockForAbstractClass(
+            \Magento\Framework\Api\SearchCriteriaInterface::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            ['create']
+        );
+        $searchCriteria->method('create')->willReturnSelf();
+        $this->searchCriteriaBuilder->method('addFilters')->willReturn($searchCriteria);
+        $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')->willReturnSelf();
+        $searchResults = $this->getMockForAbstractClass(
+            \Magento\Customer\Api\Data\GroupSearchResultsInterface::class,
+            [],
+            '',
+            false,
+            true,
+            true,
+            ['getItems']
+        );
+        $this->filterBuilder->expects($this->atLeastOnce())->method('create')->willReturnSelf();
+        $searchResults->expects($this->atLeastOnce())->method('getItems')->willReturn([]);
+        $this->customerGroupRepository
+            ->expects($this->atLeastOnce())
+            ->method('getList')
+            ->willReturn($searchResults);
+        $this->model->retrieveValidationResult([$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/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml
index 83f194bc9a2b59fd206e4678803c357966d7fdd7..023b39ddc37da1129a625835c965d2d569536bc0 100644
--- a/app/code/Magento/Catalog/etc/di.xml
+++ b/app/code/Magento/Catalog/etc/di.xml
@@ -57,6 +57,10 @@
     <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\Api\SpecialPriceStorageInterface" type="Magento\Catalog\Model\Product\Price\SpecialPriceStorage" />
+    <preference for="Magento\Catalog\Api\Data\SpecialPriceInterface" type="Magento\Catalog\Model\Product\Price\SpecialPrice" />
+    <preference for="Magento\Catalog\Api\Data\PriceUpdateResultInterface" type="Magento\Catalog\Model\Product\Price\PriceUpdateResult" />
+    <preference for="Magento\Catalog\Api\SpecialPriceInterface" type="Magento\Catalog\Model\ResourceModel\Product\Price\SpecialPrice" />
     <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" />
@@ -872,7 +876,7 @@
             </argument>
         </arguments>
     </type>
-    <type name="Magento\Catalog\Model\Product\Price\TierPriceValidator">
+    <type name="Magento\Catalog\Model\Product\Price\Validation\TierPriceValidator">
         <arguments>
             <argument name="allowedProductTypes" xsi:type="array">
                 <item name="0" xsi:type="string">simple</item>
@@ -881,4 +885,12 @@
             </argument>
         </arguments>
     </type>
+    <type name="Magento\Catalog\Model\Product\Price\SpecialPriceStorage">
+        <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>
 </config>
diff --git a/app/code/Magento/Catalog/etc/webapi.xml b/app/code/Magento/Catalog/etc/webapi.xml
index eb1167b64679df9b3f6ee126525fe51589106b83..1101a7bc93814917c3ccb95a5b60a65d846c6729 100644
--- a/app/code/Magento/Catalog/etc/webapi.xml
+++ b/app/code/Magento/Catalog/etc/webapi.xml
@@ -300,6 +300,24 @@
             <resource ref="Magento_Catalog::catalog"/>
         </resources>
     </route>
+    <route url="/V1/products/special-price-information" method="POST">
+        <service class="Magento\Catalog\Api\SpecialPriceStorageInterface" method="get"/>
+        <resources>
+            <resource ref="Magento_Catalog::catalog"/>
+        </resources>
+    </route>
+    <route url="/V1/products/special-price" method="POST">
+        <service class="Magento\Catalog\Api\SpecialPriceStorageInterface" method="update"/>
+        <resources>
+            <resource ref="Magento_Catalog::catalog"/>
+        </resources>
+    </route>
+    <route url="/V1/products/special-price-delete" method="POST">
+        <service class="Magento\Catalog\Api\SpecialPriceStorageInterface" 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/Downloadable/etc/di.xml b/app/code/Magento/Downloadable/etc/di.xml
index 62e15cddca4b53d401069989f791f762758fce72..6b5627ead2ec8c9b68e08b8ccce80e282a1fb45a 100644
--- a/app/code/Magento/Downloadable/etc/di.xml
+++ b/app/code/Magento/Downloadable/etc/di.xml
@@ -130,7 +130,7 @@
             </argument>
         </arguments>
     </type>
-    <type name="Magento\Catalog\Model\Product\Price\TierPriceValidator">
+    <type name="Magento\Catalog\Model\Product\Price\Validation\TierPriceValidator">
         <arguments>
             <argument name="allowedProductTypes" xsi:type="array">
                 <item name="3" xsi:type="string">downloadable</item>
@@ -144,4 +144,11 @@
             </argument>
         </arguments>
     </type>
+    <type name="Magento\Catalog\Model\Product\Price\SpecialPriceStorage">
+        <arguments>
+            <argument name="allowedProductTypes" xsi:type="array">
+                <item name="3" xsi:type="string">downloadable</item>
+            </argument>
+        </arguments>
+    </type>
 </config>
diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/BasePriceStorageTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/BasePriceStorageTest.php
index f9bbbd92153e916fbd6934a7946c72e6e7737769..3da02007eb5082fa8d056486670a8ec077554645 100644
--- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/BasePriceStorageTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/BasePriceStorageTest.php
@@ -7,6 +7,7 @@
 namespace Magento\Catalog\Api;
 
 use Magento\TestFramework\TestCase\WebapiAbstract;
+use Magento\Framework\Webapi\Exception as HTTPExceptionCodes;
 
 /**
  * BasePriceStorage test.
@@ -58,6 +59,40 @@ class BasePriceStorageTest extends WebapiAbstract
         $this->assertEquals($product->getSku(), $response[0]['sku']);
     }
 
+    /**
+     * Test get method, called with not existing SKU.
+     */
+    public function testGetWithInvalidSku()
+    {
+        $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',
+            ],
+        ];
+        $expected = 'Requested product doesn\'t exist: %sku';
+
+        try {
+            $this->_webApiCall($serviceInfo, ['skus' => ['sku_of_not_exiting_product']]);
+            $this->fail("Expected throwing exception");
+        } catch (\SoapFault $e) {
+            $this->assertContains(
+                $expected,
+                $e->getMessage(),
+                "SoapFault does not contain expected message."
+            );
+        } catch (\Exception $e) {
+            $error = $this->processRestExceptionResult($e);
+            $this->assertEquals($expected, $error['message']);
+            $this->assertEquals(HTTPExceptionCodes::HTTP_NOT_FOUND, $e->getCode());
+        }
+    }
+
     /**
      * Test update method.
      *
@@ -94,7 +129,62 @@ class BasePriceStorageTest extends WebapiAbstract
         /** @var \Magento\Catalog\Api\Data\ProductInterface $product */
         $product = $productRepository->get(self::SIMPLE_PRODUCT_SKU);
 
-        $this->assertNotEmpty($response);
+        $this->assertEmpty($response);
         $this->assertEquals($product->getPrice(), $newPrice);
     }
+
+    /**
+     * Test update method call with invalid parameters.
+     */
+    public function testUpdateWithInvalidParameters()
+    {
+        $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 = 9999;
+        $response = $this->_webApiCall(
+            $serviceInfo,
+            [
+                'prices' => [
+                    [
+                        'sku' => 'not_existing_sku',
+                        'price' => $newPrice,
+                        'store_id' => $storeId,
+                    ]
+                ]
+            ]
+        );
+
+        $expectedResponse = [
+            0 => [
+                'message' => 'Invalid attribute %fieldName = %fieldValue.',
+                'parameters' => [
+                    'SKU',
+                    'not_existing_sku',
+                ]
+            ],
+            1 => [
+                'message' => 'Invalid attribute %fieldName = %fieldValue.',
+                'parameters' => [
+                    'Price',
+                    '-9999',
+                ]
+            ],
+            2 => [
+                'message' => 'Requested store is not found.',
+                'parameters' => []
+            ]
+        ];
+
+        $this->assertEquals($expectedResponse, $response);
+    }
 }
diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CostStorageTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CostStorageTest.php
index d7eda7fbd58cde9f4a317c3ee7431c166fc8eebf..ff6a4f5283ff2a5f23d48da8f813df24342cc76f 100644
--- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CostStorageTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/CostStorageTest.php
@@ -7,6 +7,7 @@
 namespace Magento\Catalog\Api;
 
 use Magento\TestFramework\TestCase\WebapiAbstract;
+use Magento\Framework\Webapi\Exception as HTTPExceptionCodes;
 
 /**
  * CostStorage test.
@@ -60,6 +61,40 @@ class CostStorageTest extends WebapiAbstract
         $this->assertEquals($product->getCost(), $cost);
     }
 
+    /**
+     * Test get method, called with not existing SKUs.
+     */
+    public function testGetWithInvalidSku()
+    {
+        $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 . 'Get',
+            ],
+        ];
+        $expected = 'Requested products don\'t exist: %sku';
+
+        try {
+            $this->_webApiCall($serviceInfo, ['skus' => ['sku_of_not_exiting_product', 'invalid_sku']]);
+            $this->fail("Expected throwing exception");
+        } catch (\SoapFault $e) {
+            $this->assertContains(
+                $expected,
+                $e->getMessage(),
+                "SoapFault does not contain expected message."
+            );
+        } catch (\Exception $e) {
+            $error = $this->processRestExceptionResult($e);
+            $this->assertEquals($expected, $error['message']);
+            $this->assertEquals(HTTPExceptionCodes::HTTP_NOT_FOUND, $e->getCode());
+        }
+    }
+
     /**
      * Test update method.
      *
@@ -95,10 +130,65 @@ class CostStorageTest extends WebapiAbstract
         $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->assertEmpty($response);
         $this->assertEquals($product->getCost(), $newCost);
     }
 
+    /**
+     * Test update method call without SKU.
+     */
+    public function testUpdateWithInvalidParameters()
+    {
+        $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',
+            ],
+        ];
+        $newCost = -9999;
+        $storeId = 9999;
+        $response = $this->_webApiCall(
+            $serviceInfo,
+            [
+                'prices' => [
+                    [
+                        'sku' => 'not_existing_sku',
+                        'cost' => $newCost,
+                        'store_id' => $storeId
+                    ]
+                ]
+            ]
+        );
+
+        $expectedResponse = [
+            0 => [
+                'message' => 'Invalid attribute %fieldName = %fieldValue.',
+                'parameters' => [
+                    'SKU',
+                    'not_existing_sku',
+                ]
+            ],
+            1 => [
+                'message' => 'Invalid attribute %fieldName = %fieldValue.',
+                'parameters' => [
+                    'Cost',
+                    '-9999',
+                ]
+            ],
+            2 => [
+                'message' => 'Requested store is not found.',
+                'parameters' => []
+            ]
+        ];
+
+        $this->assertEquals($expectedResponse, $response);
+    }
+
     /**
      * Test delete method.
      *
@@ -126,4 +216,38 @@ class CostStorageTest extends WebapiAbstract
         $this->assertTrue($response);
         $this->assertNull($product->getCost());
     }
+
+    /**
+     * Test delete method, called with not existing SKUs.
+     */
+    public function testDeleteWithInvalidSku()
+    {
+        $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',
+            ],
+        ];
+        $expectedResponseMessage = 'Requested product doesn\'t exist: %sku';
+
+        try {
+            $this->_webApiCall($serviceInfo, ['skus' => ['sku_of_not_exiting_product']]);
+            $this->fail("Expected throwing exception");
+        } catch (\SoapFault $e) {
+            $this->assertContains(
+                $expectedResponseMessage,
+                $e->getMessage(),
+                "SoapFault does not contain expected message."
+            );
+        } catch (\Exception $e) {
+            $error = $this->processRestExceptionResult($e);
+            $this->assertEquals($expectedResponseMessage, $error['message']);
+            $this->assertEquals(HTTPExceptionCodes::HTTP_NOT_FOUND, $e->getCode());
+        }
+    }
 }
diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/SpecialPriceStorageTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/SpecialPriceStorageTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..bc5ebfaf68bbf53befa792c5525544da377a11cf
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/SpecialPriceStorageTest.php
@@ -0,0 +1,181 @@
+<?php
+/**
+ * Copyright © 2016 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Catalog\Api;
+
+use Magento\TestFramework\TestCase\WebapiAbstract;
+use Magento\Framework\Webapi\Exception as HTTPExceptionCodes;
+
+/**
+ * SpecialPriceStorage test.
+ */
+class SpecialPriceStorageTest extends WebapiAbstract
+{
+    const SERVICE_NAME = 'catalogSpecialPriceStorageV1';
+    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()
+    {
+        $specialPrice = 3057;
+        $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+        $product = $productRepository->get(self::SIMPLE_PRODUCT_SKU, true);
+        $product->setData('special_price', $specialPrice);
+        $productRepository->save($product);
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => '/V1/products/special-price-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]]);
+        /** @var \Magento\Catalog\Api\Data\ProductInterface $product */
+        $product = $productRepository->get(self::SIMPLE_PRODUCT_SKU);
+        $this->assertNotEmpty($response);
+        $this->assertEquals($product->getSpecialPrice(), $response[0]['price']);
+    }
+
+    /**
+     * Test get method, called with not existing SKUs.
+     */
+    public function testGetWithInvalidSku()
+    {
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => '/V1/products/special-price-information',
+                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST
+            ],
+            'soap' => [
+                'service' => self::SERVICE_NAME,
+                'serviceVersion' => self::SERVICE_VERSION,
+                'operation' => self::SERVICE_NAME . 'Get',
+            ],
+        ];
+        $expected = 'Requested products don\'t exist: %sku';
+        try {
+            $this->_webApiCall($serviceInfo, ['skus' => ['sku_of_not_exiting_product', 'invalid_sku_1']]);
+            $this->fail("Expected throwing exception");
+        } catch (\SoapFault $e) {
+            $this->assertContains(
+                $expected,
+                $e->getMessage(),
+                "SoapFault does not contain expected message."
+            );
+        } catch (\Exception $e) {
+            $error = $this->processRestExceptionResult($e);
+            $this->assertEquals($expected, $error['message']);
+            $this->assertEquals(HTTPExceptionCodes::HTTP_NOT_FOUND, $e->getCode());
+        }
+    }
+
+    /**
+     * Test update method.
+     *
+     * @magentoApiDataFixture Magento/Catalog/_files/product_virtual.php
+     */
+    public function testUpdate()
+    {
+        $sku = 'virtual-product';
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => '/V1/products/special-price',
+                '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;
+        $newPrice = 31337;
+        $response = $this->_webApiCall(
+            $serviceInfo,
+            [
+                'prices' => [
+                    [
+                        'price' => $newPrice,
+                        'store_id' => $storeId,
+                        'sku' => $sku,
+                        'price_from' => '2037-01-19 03:14:07',
+                        'price_to' => '2038-01-19 03:14:07',
+                    ]
+                ]
+            ]
+        );
+        $this->assertEmpty($response);
+    }
+
+    /**
+     * Test delete method.
+     *
+     * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php
+     */
+    public function testDelete()
+    {
+        $specialPrice = 3057;
+        /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */
+        $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
+        $fromDate = '1970-01-01 00:00:01';
+        $toDate = '2038-01-19 03:14:07';
+        $product = $productRepository->get(self::SIMPLE_PRODUCT_SKU, true);
+        $product->setData('special_price', $specialPrice)
+            ->setData('special_from_date', $fromDate)
+            ->setData('special_to_date', $toDate);
+        $productRepository->save($product);
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => '/V1/products/special-price-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' => [
+                    [
+                        'price' => $specialPrice,
+                        'store_id' => 0,
+                        'sku' => self::SIMPLE_PRODUCT_SKU,
+                        'price_from' => $fromDate,
+                        'price_to' => $toDate,
+                    ]
+                ]
+            ]
+        );
+        /** @var \Magento\Catalog\Api\Data\ProductInterface $product */
+        $product = $productRepository->get(self::SIMPLE_PRODUCT_SKU, false, null, true);
+        $this->assertEmpty($response);
+        $this->assertNull($product->getSpecialPrice());
+    }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/TierPriceStorageTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/TierPriceStorageTest.php
index da72fbe1005d1c6aa8e6423215cbdd27f46d3fc0..bf9d65af13e50d2d7bf36a74af82faf19b15d9b4 100644
--- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/TierPriceStorageTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/TierPriceStorageTest.php
@@ -93,17 +93,59 @@ class TierPriceStorageTest extends WebapiAbstract
             'price_type' => \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_FIXED,
             'website_id' => 0,
             'sku' => self::SIMPLE_PRODUCT_SKU,
-            'customer_group' => 'ALL GROUPS',
+            'customer_group' => 'not logged in',
             '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->assertEmpty($response);
         $this->assertTrue($this->isPriceCorrect($newPrice, $tierPrices));
         $this->assertTrue($this->isPriceCorrect($updatedPrice, $tierPrices));
     }
 
+    /**
+     * Call update method with specifying new website value for tier price with all websites value.
+     *
+     * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php
+     */
+    public function testUpdateWebsiteForAllWebsites()
+    {
+        $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',
+            ],
+        ];
+        $invalidPrice = [
+            'price' => 40,
+            'price_type' => \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_FIXED,
+            'website_id' => 2,
+            'sku' => self::SIMPLE_PRODUCT_SKU,
+            'customer_group' => 'not logged in',
+            'quantity' => 3
+        ];
+        $response = $this->_webApiCall($serviceInfo, ['prices' => [$invalidPrice]]);
+        $this->assertNotEmpty($response);
+        $this->assertEquals('Invalid attribute %fieldName = %fieldValue.', $response[0]['message']);
+        $this->assertEquals('Website Id', $response[0]['parameters'][0]);
+        $this->assertEquals('2', $response[0]['parameters'][1]);
+        $message = 'We found a duplicate website, tier price, customer group and quantity: %fieldName1 '
+            . '= %fieldValue1, %fieldName2 = %fieldValue2, %fieldName3 = %fieldValue3.';
+        $this->assertEquals($message, $response[1]['message']);
+        $this->assertEquals('Customer Group', $response[1]['parameters'][0]);
+        $this->assertEquals('NOT LOGGED IN', $response[1]['parameters'][1]);
+        $this->assertEquals('Website Id', $response[1]['parameters'][2]);
+        $this->assertEquals('0', $response[1]['parameters'][3]);
+        $this->assertEquals('Quantity', $response[1]['parameters'][4]);
+        $this->assertEquals('3.0000', $response[1]['parameters'][5]);
+    }
+
     /**
      * Test replace method.
      *
@@ -136,7 +178,7 @@ class TierPriceStorageTest extends WebapiAbstract
                 'price_type' => \Magento\Catalog\Api\Data\TierPriceInterface::PRICE_TYPE_FIXED,
                 'website_id' => 0,
                 'sku' => self::SIMPLE_PRODUCT_SKU,
-                'customer_group' => 'general',
+                'customer_group' => 'not logged in',
                 'quantity' => 33
             ]
         ];
@@ -144,12 +186,8 @@ class TierPriceStorageTest extends WebapiAbstract
         $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->assertEmpty($response);
         $this->assertEquals(count($newPrices), count($tierPrices));
-
-        foreach ($newPrices as $newPrice) {
-            $this->assertTrue($this->isPriceCorrect($newPrice, $tierPrices));
-        }
     }
 
     /**
@@ -197,7 +235,7 @@ class TierPriceStorageTest extends WebapiAbstract
         $productRepository = $this->objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
         $tierPrices = $productRepository->get(self::SIMPLE_PRODUCT_SKU)->getTierPrices();
         $tierPrice = $tierPrices[0];
-        $this->assertTrue($response);
+        $this->assertEmpty($response);
         $this->assertEquals(1, count($tierPrices));
         $this->assertEquals($pricesToStore, $tierPrice);
     }
@@ -223,6 +261,7 @@ class TierPriceStorageTest extends WebapiAbstract
                 && $tierPrice->getExtensionAttributes()->getWebsiteId() == $price['website_id']
             ) {
                 $isCorrect = true;
+                break;
             }
         }