diff --git a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Place.php b/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Place.php index 7f229d87cc275..0ffe6df37b1f0 100644 --- a/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Place.php +++ b/app/code/Magento/Authorizenet/Controller/Directpost/Payment/Place.php @@ -127,7 +127,7 @@ protected function placeCheckoutOrder() ); } catch (\Exception $exception) { $result->setData('error', true); - $result->setData('error_messages', __('Cannot place order.')); + $result->setData('error_messages', __('Unable to place order. Please try again later.')); } if ($response instanceof Http) { $response->representJson($this->jsonHelper->jsonEncode($result)); diff --git a/app/code/Magento/Authorizenet/Test/Unit/Controller/Directpost/Payment/PlaceTest.php b/app/code/Magento/Authorizenet/Test/Unit/Controller/Directpost/Payment/PlaceTest.php index bb423bc8d54d7..ee0911ca68158 100644 --- a/app/code/Magento/Authorizenet/Test/Unit/Controller/Directpost/Payment/PlaceTest.php +++ b/app/code/Magento/Authorizenet/Test/Unit/Controller/Directpost/Payment/PlaceTest.php @@ -279,7 +279,7 @@ public function textExecuteFailedPlaceOrderDataProvider() { $objectFailed = new \Magento\Framework\DataObject(); $objectFailed->setData('error', true); - $objectFailed->setData('error_messages', __('Cannot place order.')); + $objectFailed->setData('error_messages', __('Unable to place order. Please try again later.')); return [ [ diff --git a/app/code/Magento/Authorizenet/composer.json b/app/code/Magento/Authorizenet/composer.json index bd98c50643d0d..75d15827744e0 100644 --- a/app/code/Magento/Authorizenet/composer.json +++ b/app/code/Magento/Authorizenet/composer.json @@ -13,7 +13,7 @@ "magento/framework": "100.0.*" }, "type": "magento2-module", - "version": "100.0.6", + "version": "100.0.7", "license": [ "proprietary" ], diff --git a/app/code/Magento/Authorizenet/i18n/en_US.csv b/app/code/Magento/Authorizenet/i18n/en_US.csv index 7bbbbaf286d5a..d77495536d108 100644 --- a/app/code/Magento/Authorizenet/i18n/en_US.csv +++ b/app/code/Magento/Authorizenet/i18n/en_US.csv @@ -13,6 +13,7 @@ Cancel,Cancel "Something went wrong canceling the transactions.","Something went wrong canceling the transactions." "There was an error canceling transactions. Please contact us or try again later.","There was an error canceling transactions. Please contact us or try again later." "We couldn't process your order right now. Please try again later.","We couldn't process your order right now. Please try again later." +"Unable to place order. Please try again later.","Unable to place order. Please try again later." "amount %1","amount %1" failed,failed successful,successful diff --git a/app/code/Magento/Backend/Block/System/Store/Edit/Form/Website.php b/app/code/Magento/Backend/Block/System/Store/Edit/Form/Website.php index 61daa4a39a15a..623027030c17a 100644 --- a/app/code/Magento/Backend/Block/System/Store/Edit/Form/Website.php +++ b/app/code/Magento/Backend/Block/System/Store/Edit/Form/Website.php @@ -108,7 +108,7 @@ protected function _prepareStoreFieldset(\Magento\Framework\Data\Form $form) ); } - if (!$websiteModel->getIsDefault() && $websiteModel->getStoresCount()) { + if ($this->checkIsSingleAndIsDefaultStore($websiteModel)) { $fieldset->addField( 'is_default', 'checkbox', @@ -133,4 +133,12 @@ protected function _prepareStoreFieldset(\Magento\Framework\Data\Form $form) ['name' => 'website[website_id]', 'value' => $websiteModel->getId()] ); } + + private function checkIsSingleAndIsDefaultStore($websiteModel) + { + $hasOnlyDefaultStore = $websiteModel->getStoresCount() == 1 && + isset($websiteModel->getStoreIds()[\Magento\Store\Model\Store::DEFAULT_STORE_ID]); + + return !$websiteModel->getIsDefault() && $websiteModel->getStoresCount() && !$hasOnlyDefaultStore; + } } diff --git a/app/code/Magento/Braintree/Model/PaymentMethod.php b/app/code/Magento/Braintree/Model/PaymentMethod.php index 3f85a3d79b563..4c263888bd4a8 100644 --- a/app/code/Magento/Braintree/Model/PaymentMethod.php +++ b/app/code/Magento/Braintree/Model/PaymentMethod.php @@ -269,6 +269,7 @@ public function assignData(\Magento\Framework\DataObject $data) $infoInstance->setAdditionalInformation('cc_last4', $additionalData->getData('cc_last4')); $infoInstance->setAdditionalInformation('cc_token', $additionalData->getData('cc_token')); + $infoInstance->setAdditionalInformation('store_in_vault', $additionalData->getData('store_in_vault')); $infoInstance->setAdditionalInformation( 'payment_method_nonce', $additionalData->getData('payment_method_nonce') diff --git a/app/code/Magento/Braintree/Test/Unit/Model/PaymentMethodTest.php b/app/code/Magento/Braintree/Test/Unit/Model/PaymentMethodTest.php index 29e45a4016bca..1a6eaf854c9a0 100644 --- a/app/code/Magento/Braintree/Test/Unit/Model/PaymentMethodTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Model/PaymentMethodTest.php @@ -276,17 +276,25 @@ public function testAssignData() ->with($ccExpYear) ->willReturnSelf(); - $this->infoInstanceMock->expects($this->atLeastOnce()) + $this->infoInstanceMock->expects($this->at(0)) ->method('setAdditionalInformation') - ->willReturnMap( - [ - ['device_data', $deviceData], - ['cc_last4', $ccLast4], - ['cc_token', $ccToken], - ['payment_method_nonce', $paymentMethodNonce], - ['store_in_vault', $storeInVault] - ] - ); + ->with('device_data', $deviceData); + + $this->infoInstanceMock->expects($this->at(1)) + ->method('setAdditionalInformation') + ->with('cc_last4', $ccLast4); + + $this->infoInstanceMock->expects($this->at(2)) + ->method('setAdditionalInformation') + ->with('cc_token', $ccToken); + + $this->infoInstanceMock->expects($this->at(3)) + ->method('setAdditionalInformation') + ->with('store_in_vault', $storeInVault); + + $this->infoInstanceMock->expects($this->at(4)) + ->method('setAdditionalInformation') + ->with('payment_method_nonce', $paymentMethodNonce); $this->model->assignData($data); } diff --git a/app/code/Magento/Braintree/composer.json b/app/code/Magento/Braintree/composer.json index 7d0abd2619bca..93360c52785eb 100644 --- a/app/code/Magento/Braintree/composer.json +++ b/app/code/Magento/Braintree/composer.json @@ -22,7 +22,7 @@ "magento/module-checkout-agreements": "100.0.*" }, "type": "magento2-module", - "version": "100.0.7", + "version": "100.0.8", "license": [ "proprietary" ], diff --git a/app/code/Magento/BundleImportExport/composer.json b/app/code/Magento/BundleImportExport/composer.json index c15ecbc12367e..b9f1e06670455 100644 --- a/app/code/Magento/BundleImportExport/composer.json +++ b/app/code/Magento/BundleImportExport/composer.json @@ -11,7 +11,7 @@ "magento/framework": "100.0.*" }, "type": "magento2-module", - "version": "100.0.5", + "version": "100.0.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/CacheInvalidate/composer.json b/app/code/Magento/CacheInvalidate/composer.json index 7adbd2e11354b..5b7988809fd86 100644 --- a/app/code/Magento/CacheInvalidate/composer.json +++ b/app/code/Magento/CacheInvalidate/composer.json @@ -7,7 +7,7 @@ "magento/framework": "100.0.*" }, "type": "magento2-module", - "version": "100.0.5", + "version": "100.0.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Catalog/Block/Product/View.php b/app/code/Magento/Catalog/Block/Product/View.php index a42ba1dd55202..5f25a87bb2a73 100644 --- a/app/code/Magento/Catalog/Block/Product/View.php +++ b/app/code/Magento/Catalog/Block/Product/View.php @@ -232,26 +232,28 @@ public function getJsonConfig() foreach ($tierPricesList as $tierPrice) { $tierPrices[] = $this->priceCurrency->convert($tierPrice['price']->getValue()); } + $regularPriceAmount = $product->getPriceInfo()->getPrice('regular_price')->getAmount(); + $finalPriceAmount = $product->getPriceInfo()->getPrice('final_price')->getAmount(); $config = [ 'productId' => $product->getId(), 'priceFormat' => $this->_localeFormat->getPriceFormat(), 'prices' => [ 'oldPrice' => [ - 'amount' => $this->priceCurrency->convert( - $product->getPriceInfo()->getPrice('regular_price')->getAmount()->getValue() - ), + 'amount' => $regularPriceAmount + ? $this->priceCurrency->convert($regularPriceAmount->getValue()) + : null, 'adjustments' => [] ], 'basePrice' => [ - 'amount' => $this->priceCurrency->convert( - $product->getPriceInfo()->getPrice('final_price')->getAmount()->getBaseAmount() - ), + 'amount' => $finalPriceAmount + ? $this->priceCurrency->convert($finalPriceAmount->getBaseAmount()) + : null, 'adjustments' => [] ], 'finalPrice' => [ - 'amount' => $this->priceCurrency->convert( - $product->getPriceInfo()->getPrice('final_price')->getAmount()->getValue() - ), + 'amount' => $finalPriceAmount + ? $this->priceCurrency->convert($finalPriceAmount->getValue()) + : null, 'adjustments' => [] ] ], diff --git a/app/code/Magento/Catalog/Model/Product/Pricing/Renderer/SalableResolver.php b/app/code/Magento/Catalog/Model/Product/Pricing/Renderer/SalableResolver.php new file mode 100644 index 0000000000000..e7b30d76f66b5 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Pricing/Renderer/SalableResolver.php @@ -0,0 +1,24 @@ +getCanShowPrice() !== false && $salableItem->isSalable(); + } +} diff --git a/app/code/Magento/Catalog/Model/Product/Pricing/Renderer/SalableResolverInterface.php b/app/code/Magento/Catalog/Model/Product/Pricing/Renderer/SalableResolverInterface.php new file mode 100644 index 0000000000000..1ad37292611d5 --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Pricing/Renderer/SalableResolverInterface.php @@ -0,0 +1,21 @@ +salableResolver = $salableResolver ?: \Magento\Framework\App\ObjectManager::getInstance() + ->get(SalableResolverInterface::class); + } + /** * @return string */ protected function _toHtml() { - if (!$this->getSaleableItem() || $this->getSaleableItem()->getCanShowPrice() === false) { + if (!$this->salableResolver->isSalable($this->getSaleableItem())) { return ''; } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Pricing/Renderer/SalableResolverTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Pricing/Renderer/SalableResolverTest.php new file mode 100644 index 0000000000000..48bab07fb3ad7 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Pricing/Renderer/SalableResolverTest.php @@ -0,0 +1,60 @@ +product = $this->getMock( + 'Magento\Catalog\Model\Product', + ['__wakeup', 'getCanShowPrice', 'isSalable'], + [], + '', + false + ); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->object = $objectManager->getObject( + 'Magento\Catalog\Model\Product\Pricing\Renderer\SalableResolver' + ); + } + + public function testSalableItem() + { + $this->product->expects($this->any()) + ->method('getCanShowPrice') + ->willReturn(true); + + $this->product->expects($this->any())->method('isSalable')->willReturn(true); + + $result = $this->object->isSalable($this->product); + $this->assertTrue($result); + } + + public function testNotSalableItem() + { + $this->product->expects($this->any()) + ->method('getCanShowPrice') + ->willReturn(true); + + $this->product->expects($this->any())->method('isSalable')->willReturn(false); + + $result = $this->object->isSalable($this->product); + $this->assertFalse($result); + } +} diff --git a/app/code/Magento/Catalog/Test/Unit/Pricing/Render/FinalPriceBoxTest.php b/app/code/Magento/Catalog/Test/Unit/Pricing/Render/FinalPriceBoxTest.php index 3abdcec5f8fe1..02add5a8fa377 100644 --- a/app/code/Magento/Catalog/Test/Unit/Pricing/Render/FinalPriceBoxTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Pricing/Render/FinalPriceBoxTest.php @@ -6,6 +6,8 @@ namespace Magento\Catalog\Test\Unit\Pricing\Render; +use Magento\Catalog\Model\Product\Pricing\Renderer\SalableResolverInterface; + /** * Class FinalPriceBoxTest */ @@ -44,7 +46,7 @@ class FinalPriceBoxTest extends \PHPUnit_Framework_TestCase /** * @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $loggerMock; + protected $logger; /** * @var \Magento\Framework\Pricing\Render\RendererPool|\PHPUnit_Framework_MockObject_MockObject @@ -56,12 +58,17 @@ class FinalPriceBoxTest extends \PHPUnit_Framework_TestCase */ protected $price; + /** + * @var SalableResolverInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $salableResolverMock; + protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->product = $this->getMock( 'Magento\Catalog\Model\Product', - ['getPriceInfo', '__wakeup', 'getCanShowPrice'], + ['getPriceInfo', '__wakeup', 'getCanShowPrice', 'isSalable'], [], '', false @@ -74,11 +81,9 @@ protected function setUp() $this->layout = $this->getMock('Magento\Framework\View\Layout', [], [], '', false); $this->priceBox = $this->getMock('Magento\Framework\Pricing\Render\PriceBox', [], [], '', false); - $this->loggerMock = $this->getMock('Psr\Log\LoggerInterface'); + $this->logger = $this->getMock('Psr\Log\LoggerInterface'); - $this->layout->expects($this->any()) - ->method('getBlock') - ->will($this->returnValue($this->priceBox)); + $this->layout->expects($this->any())->method('getBlock')->willReturn($this->priceBox); $store = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class) ->getMockForAbstractClass(); @@ -92,7 +97,7 @@ protected function setUp() 'Magento\Framework\View\Element\Template\Context', [ 'storeManager' => $storeManagerMock, - 'logger' => $this->loggerMock + 'logger' => $this->logger ] ); @@ -105,6 +110,12 @@ protected function setUp() ->method('getPriceCode') ->will($this->returnValue(\Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE)); + $this->salableResolverMock = $this->getMockBuilder( + 'Magento\Catalog\Model\Product\Pricing\Renderer\SalableResolverInterface' + ) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->object = $objectManager->getObject( 'Magento\Catalog\Pricing\Render\FinalPriceBox', [ @@ -112,7 +123,8 @@ protected function setUp() 'saleableItem' => $this->product, 'rendererPool' => $this->rendererPool, 'price' => $this->price, - 'data' => ['zone' => 'test_zone', 'list_category_page' => true] + 'data' => ['zone' => 'test_zone', 'list_category_page' => true], + 'salableResolver' => $this->salableResolverMock ] ); } @@ -130,6 +142,8 @@ public function testRenderMsrpDisabled() ->with($this->equalTo($this->product)) ->will($this->returnValue(false)); + $this->salableResolverMock->expects($this->once())->method('isSalable')->with($this->product)->willReturn(true); + $result = $this->object->toHtml(); //assert price wrapper @@ -138,6 +152,18 @@ public function testRenderMsrpDisabled() $this->assertRegExp('/[final_price]/', $result); } + public function testNotSalableItem() + { + $this->salableResolverMock + ->expects($this->once()) + ->method('isSalable') + ->with($this->product) + ->willReturn(false); + $result = $this->object->toHtml(); + + $this->assertEmpty($result); + } + public function testRenderMsrpEnabled() { $priceType = $this->getMock('Magento\Msrp\Pricing\Price\MsrpPrice', [], [], '', false); @@ -172,6 +198,8 @@ public function testRenderMsrpEnabled() ->with('msrp_price', $this->product, $arguments) ->will($this->returnValue($priceBoxRender)); + $this->salableResolverMock->expects($this->once())->method('isSalable')->with($this->product)->willReturn(true); + $result = $this->object->toHtml(); //assert price wrapper @@ -183,7 +211,7 @@ public function testRenderMsrpEnabled() public function testRenderMsrpNotRegisteredException() { - $this->loggerMock->expects($this->once()) + $this->logger->expects($this->once()) ->method('critical'); $this->priceInfo->expects($this->once()) @@ -191,6 +219,8 @@ public function testRenderMsrpNotRegisteredException() ->with($this->equalTo('msrp_price')) ->will($this->throwException(new \InvalidArgumentException())); + $this->salableResolverMock->expects($this->once())->method('isSalable')->with($this->product)->willReturn(true); + $result = $this->object->toHtml(); //assert price wrapper diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index e8b088b19cb16..ceaa51b70ecdb 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -43,6 +43,7 @@ + diff --git a/app/code/Magento/CatalogInventory/composer.json b/app/code/Magento/CatalogInventory/composer.json index 415a3bd6d0fcf..548dc6193232c 100644 --- a/app/code/Magento/CatalogInventory/composer.json +++ b/app/code/Magento/CatalogInventory/composer.json @@ -13,7 +13,7 @@ "magento/module-ui": "100.0.*" }, "type": "magento2-module", - "version": "100.0.6", + "version": "100.0.7", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/CatalogInventory/etc/events.xml b/app/code/Magento/CatalogInventory/etc/events.xml index a1476c2c3f8b1..d9db59b7a1766 100644 --- a/app/code/Magento/CatalogInventory/etc/events.xml +++ b/app/code/Magento/CatalogInventory/etc/events.xml @@ -33,9 +33,6 @@ - - - diff --git a/app/code/Magento/CatalogSearch/composer.json b/app/code/Magento/CatalogSearch/composer.json index 2f626236349ea..bd29bab0af7c0 100644 --- a/app/code/Magento/CatalogSearch/composer.json +++ b/app/code/Magento/CatalogSearch/composer.json @@ -14,7 +14,7 @@ "magento/framework": "100.0.*" }, "type": "magento2-module", - "version": "100.0.5", + "version": "100.0.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php index 2b7bfe017455b..c514483843663 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/ProductProcessUrlRewriteSavingObserver.php @@ -55,9 +55,7 @@ public function execute(\Magento\Framework\Event\Observer $observer) UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, ]); } - if (!in_array($product->getOrigData('visibility'), $product->getVisibleInSiteVisibilities())) { - $this->urlPersist->replace($this->productUrlRewriteGenerator->generate($product)); - } + $this->urlPersist->replace($this->productUrlRewriteGenerator->generate($product)); } } } diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php new file mode 100644 index 0000000000000..88207c11fe614 --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/ProductProcessUrlRewriteSavingObserverTest.php @@ -0,0 +1,140 @@ +urlPersist = $this->getMockBuilder(UrlPersistInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->productUrlRewriteGenerator = $this->getMockBuilder(ProductUrlRewriteGenerator::class) + ->disableOriginalConstructor() + ->getMock(); + + $objectManagerHelper = new ObjectManagerHelper($this); + $this->productProcessUrlRewriteSavingObserver = $objectManagerHelper->getObject( + ProductProcessUrlRewriteSavingObserver::class, + [ + 'productUrlRewriteGenerator' => $this->productUrlRewriteGenerator, + 'urlPersist' => $this->urlPersist, + ] + ); + + $this->product = $this->getMockBuilder(Product::class) + ->disableOriginalConstructor() + ->setMethods([ + 'getProductCategories' + ]) + ->getMock(); + + $event = $this->getMock(Event::class, ['getProduct'], [], '', false); + $event->method('getProduct')->willReturn($this->product); + + $this->observer = $this->getMock(Observer::class, ['getEvent'], [], '', false); + $this->observer->method('getEvent')->willReturn($event); + + } + + /** + * @dataProvider visibilityProvider + * @param integer $before + * @param integer $after + */ + public function testVisibility($before, $after) + { + $this->product->setOrigData('visibility', $before); + $this->product->setData('visibility', $after); + + $this->productUrlRewriteGenerator->expects(static::once()) + ->method('generate') + ->with($this->product) + ->willReturn(['test']); + $this->urlPersist->expects(static::once()) + ->method('replace') + ->with(['test']); + + $this->productProcessUrlRewriteSavingObserver->execute($this->observer); + } + + public function visibilityProvider() + { + return [ + ['origData' => Visibility::VISIBILITY_NOT_VISIBLE, 'data' => Visibility::VISIBILITY_IN_CATALOG], + ['origData' => null, 'data' => Visibility::VISIBILITY_IN_CATALOG], + ['origData' => Visibility::VISIBILITY_BOTH, 'data' => Visibility::VISIBILITY_IN_SEARCH], + ['origData' => Visibility::VISIBILITY_IN_CATALOG, 'data' => Visibility::VISIBILITY_NOT_VISIBLE], + ['origData' => null, 'data' => Visibility::VISIBILITY_NOT_VISIBLE], + ]; + } + + /** + * @dataProvider notVisibilityProvider + * @param integer $before + * @param integer $after + */ + public function testNotVisibility($before, $after) + { + $this->product->setOrigData('visibility', $before); + $this->product->setData('visibility', $after); + + $this->productUrlRewriteGenerator->expects(static::never())->method('generate'); + $this->urlPersist->expects(static::never())->method('replace'); + + $this->productProcessUrlRewriteSavingObserver->execute($this->observer); + } + + public function notVisibilityProvider() + { + return [ + ['origData' => Visibility::VISIBILITY_IN_SEARCH, 'data' => Visibility::VISIBILITY_IN_SEARCH], + ]; + } +} diff --git a/app/code/Magento/CatalogUrlRewrite/composer.json b/app/code/Magento/CatalogUrlRewrite/composer.json index ab158b4a6adad..fa75512dc4f18 100644 --- a/app/code/Magento/CatalogUrlRewrite/composer.json +++ b/app/code/Magento/CatalogUrlRewrite/composer.json @@ -13,7 +13,7 @@ "magento/framework": "100.0.*" }, "type": "magento2-module", - "version": "100.0.6", + "version": "100.0.7", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/CatalogUrlRewrite/etc/events.xml b/app/code/Magento/CatalogUrlRewrite/etc/events.xml index 02c67bb500b66..18ad4bea3af45 100644 --- a/app/code/Magento/CatalogUrlRewrite/etc/events.xml +++ b/app/code/Magento/CatalogUrlRewrite/etc/events.xml @@ -33,4 +33,7 @@ + + + diff --git a/app/code/Magento/ConfigurableProduct/Pricing/Price/ConfigurablePriceResolver.php b/app/code/Magento/ConfigurableProduct/Pricing/Price/ConfigurablePriceResolver.php index 68e82ed76a23f..eb3040ad2f668 100644 --- a/app/code/Magento/ConfigurableProduct/Pricing/Price/ConfigurablePriceResolver.php +++ b/app/code/Magento/ConfigurableProduct/Pricing/Price/ConfigurablePriceResolver.php @@ -64,11 +64,6 @@ public function resolvePrice(\Magento\Framework\Pricing\SaleableInterface $produ $productPrice = $this->priceResolver->resolvePrice($subProduct); $price = $price ? min($price, $productPrice) : $productPrice; } - if ($price === null) { - throw new \Magento\Framework\Exception\LocalizedException( - __('Configurable product "%1" does not have sub-products', $product->getSku()) - ); - } return (float)$price; } diff --git a/app/code/Magento/ConfigurableProduct/Pricing/Render/FinalPriceBox.php b/app/code/Magento/ConfigurableProduct/Pricing/Render/FinalPriceBox.php index 19bf0eee888f1..011743e856444 100644 --- a/app/code/Magento/ConfigurableProduct/Pricing/Render/FinalPriceBox.php +++ b/app/code/Magento/ConfigurableProduct/Pricing/Render/FinalPriceBox.php @@ -5,6 +5,7 @@ */ namespace Magento\ConfigurableProduct\Pricing\Render; +use Magento\Catalog\Model\Product\Pricing\Renderer\SalableResolverInterface; use Magento\Catalog\Pricing\Price\FinalPrice; use Magento\Catalog\Pricing\Price\RegularPrice; use Magento\ConfigurableProduct\Pricing\Price\ConfigurableOptionsProviderInterface; @@ -30,6 +31,7 @@ class FinalPriceBox extends \Magento\Catalog\Pricing\Render\FinalPriceBox * @param ConfigurableOptionsProviderInterface $configurableOptionsProvider * @param array $data * @param LowestPriceOptionsProviderInterface $lowestPriceOptionsProvider + * @param SalableResolverInterface $salableResolver * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( @@ -39,9 +41,10 @@ public function __construct( RendererPool $rendererPool, ConfigurableOptionsProviderInterface $configurableOptionsProvider, array $data = [], - LowestPriceOptionsProviderInterface $lowestPriceOptionsProvider = null + LowestPriceOptionsProviderInterface $lowestPriceOptionsProvider = null, + SalableResolverInterface $salableResolver = null ) { - parent::__construct($context, $saleableItem, $price, $rendererPool, $data); + parent::__construct($context, $saleableItem, $price, $rendererPool, $data, $salableResolver); $this->lowestPriceOptionsProvider = $lowestPriceOptionsProvider ?: ObjectManager::getInstance()->get(LowestPriceOptionsProviderInterface::class); } diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Price/ConfigurablePriceResolverTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Price/ConfigurablePriceResolverTest.php index 8db61bb5e0a43..78ac3a3097458 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Price/ConfigurablePriceResolverTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Price/ConfigurablePriceResolverTest.php @@ -51,24 +51,6 @@ protected function setUp() ); } - /** - * situation: There are no used products, thus there are no prices - * - * @expectedException \Magento\Framework\Exception\LocalizedException - */ - public function testResolvePriceWithNoPrices() - { - $product = $this->getMockBuilder( - \Magento\Catalog\Model\Product::class - )->disableOriginalConstructor()->getMock(); - - $product->expects($this->once())->method('getSku')->willReturn('Kiwi'); - - $this->lowestPriceOptionsProvider->expects($this->once())->method('getProducts')->willReturn([]); - - $this->resolver->resolvePrice($product); - } - /** * situation: one product is supplying the price, which could be a price of zero (0) * diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Render/FinalPriceBoxTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Render/FinalPriceBoxTest.php index b102e1d81f48e..f757d483a1897 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Render/FinalPriceBoxTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Pricing/Render/FinalPriceBoxTest.php @@ -3,139 +3,407 @@ * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\ConfigurableProduct\Test\Unit\Pricing\Render; -use Magento\Catalog\Pricing\Price\FinalPrice; -use Magento\Catalog\Pricing\Price\RegularPrice; -use Magento\ConfigurableProduct\Pricing\Price\LowestPriceOptionsProviderInterface; -use Magento\ConfigurableProduct\Pricing\Render\FinalPriceBox; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Catalog\Model\Product\Pricing\Renderer\SalableResolverInterface; +/** + * Class FinalPriceBoxTest + */ class FinalPriceBoxTest extends \PHPUnit_Framework_TestCase { /** - * @var \Magento\Framework\View\Element\Template\Context|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Catalog\Pricing\Render\FinalPriceBox + */ + protected $object; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $priceType; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $priceInfo; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $priceBox; + + /** + * @var \Magento\Framework\View\LayoutInterface | \PHPUnit_Framework_MockObject_MockObject */ - private $context; + protected $layout; /** * @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject */ - private $saleableItem; + protected $product; /** - * @var \Magento\Framework\Pricing\Price\PriceInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject */ - private $price; + protected $logger; /** * @var \Magento\Framework\Pricing\Render\RendererPool|\PHPUnit_Framework_MockObject_MockObject */ - private $rendererPool; + protected $rendererPool; /** - * @var LowestPriceOptionsProviderInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Pricing\Price\PriceInterface|\PHPUnit_Framework_MockObject_MockObject */ - private $lowestPriceOptionsProvider; + protected $price; /** - * @var FinalPriceBox + * @var SalableResolverInterface|\PHPUnit_Framework_MockObject_MockObject */ - private $model; + private $salableResolverMock; protected function setUp() { - $this->context = $this->getMockBuilder(\Magento\Framework\View\Element\Template\Context::class) + $this->product = $this->getMock( + \Magento\Catalog\Model\Product::class, + ['getPriceInfo', '__wakeup', 'getCanShowPrice', 'isSalable'], + [], + '', + false + ); + $this->priceInfo = $this->getMock('Magento\Framework\Pricing\PriceInfo', ['getPrice'], [], '', false); + $this->product->expects($this->any()) + ->method('getPriceInfo') + ->will($this->returnValue($this->priceInfo)); + + $eventManager = $this->getMock('Magento\Framework\Event\Test\Unit\ManagerStub', [], [], '', false); + $config = $this->getMock('Magento\Store\Model\Store\Config', [], [], '', false); + $this->layout = $this->getMock('Magento\Framework\View\Layout', [], [], '', false); + + $this->priceBox = $this->getMock('Magento\Framework\Pricing\Render\PriceBox', [], [], '', false); + $this->logger = $this->getMock('Psr\Log\LoggerInterface'); + + $this->layout->expects($this->any())->method('getBlock')->willReturn($this->priceBox); + + $cacheState = $this->getMockBuilder(\Magento\Framework\App\Cache\StateInterface::class) + ->getMockForAbstractClass(); + + $appState = $this->getMockBuilder(\Magento\Framework\App\State::class) ->disableOriginalConstructor() ->getMock(); - $this->saleableItem = $this->getMockBuilder(\Magento\Catalog\Model\Product::class) + $resolver = $this->getMockBuilder(\Magento\Framework\View\Element\Template\File\Resolver::class) ->disableOriginalConstructor() ->getMock(); - $this->price = $this->getMockBuilder(\Magento\Framework\Pricing\Price\PriceInterface::class) + $urlBuilder = $this->getMockBuilder(\Magento\Framework\UrlInterface::class)->getMockForAbstractClass(); + + $store = $this->getMockBuilder(\Magento\Store\Api\Data\StoreInterface::class)->getMockForAbstractClass(); + $storeManager = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) + ->setMethods(['getStore', 'getCode']) ->getMockForAbstractClass(); + $storeManager->expects($this->any())->method('getStore')->will($this->returnValue($store)); + + $scopeConfigMock = $this->getMockForAbstractClass('Magento\Framework\App\Config\ScopeConfigInterface'); + $context = $this->getMock('Magento\Framework\View\Element\Template\Context', [], [], '', false); + $context->expects($this->any()) + ->method('getEventManager') + ->will($this->returnValue($eventManager)); + $context->expects($this->any()) + ->method('getStoreConfig') + ->will($this->returnValue($config)); + $context->expects($this->any()) + ->method('getLayout') + ->will($this->returnValue($this->layout)); + $context->expects($this->any()) + ->method('getLogger') + ->will($this->returnValue($this->logger)); + $context->expects($this->any()) + ->method('getScopeConfig') + ->will($this->returnValue($scopeConfigMock)); + $context->expects($this->any()) + ->method('getCacheState') + ->will($this->returnValue($cacheState)); + $context->expects($this->any()) + ->method('getStoreManager') + ->will($this->returnValue($storeManager)); + $context->expects($this->any()) + ->method('getAppState') + ->will($this->returnValue($appState)); + $context->expects($this->any()) + ->method('getResolver') + ->will($this->returnValue($resolver)); + $context->expects($this->any()) + ->method('getUrlBuilder') + ->will($this->returnValue($urlBuilder)); - $this->rendererPool = $this->getMockBuilder(\Magento\Framework\Pricing\Render\RendererPool::class) + $this->rendererPool = $this->getMockBuilder('Magento\Framework\Pricing\Render\RendererPool') ->disableOriginalConstructor() ->getMock(); - $this->lowestPriceOptionsProvider = $this->getMockBuilder(LowestPriceOptionsProviderInterface::class) + $this->price = $this->getMock('Magento\Framework\Pricing\Price\PriceInterface'); + $this->price->expects($this->any()) + ->method('getPriceCode') + ->will($this->returnValue(\Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE)); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->salableResolverMock = $this->getMockBuilder(SalableResolverInterface::class) + ->disableOriginalConstructor() ->getMockForAbstractClass(); - $this->model = (new ObjectManager($this))->getObject( - FinalPriceBox::class, + $this->object = $objectManager->getObject( + 'Magento\Catalog\Pricing\Render\FinalPriceBox', [ - 'context' => $this->context, - 'saleableItem' => $this->saleableItem, - 'price' => $this->price, + 'context' => $context, + 'saleableItem' => $this->product, 'rendererPool' => $this->rendererPool, - 'lowestPriceOptionsProvider' => $this->lowestPriceOptionsProvider, + 'price' => $this->price, + 'data' => ['zone' => 'test_zone', 'list_category_page' => true], + 'salableResolver' => $this->salableResolverMock ] ); } - /** - * @param float $regularPrice - * @param float $finalPrice - * @param bool $expected - * @dataProvider hasSpecialPriceDataProvider - */ - public function testHasSpecialPrice( - $regularPrice, - $finalPrice, - $expected - ) { - $priceMockOne = $this->getMockBuilder(\Magento\Framework\Pricing\Price\PriceInterface::class) - ->getMockForAbstractClass(); + public function testRenderMsrpDisabled() + { + $priceType = $this->getMock('Magento\Msrp\Pricing\Price\MsrpPrice', [], [], '', false); + $this->priceInfo->expects($this->once()) + ->method('getPrice') + ->with($this->equalTo('msrp_price')) + ->will($this->returnValue($priceType)); - $priceMockOne->expects($this->once()) - ->method('getValue') - ->willReturn($regularPrice); + $priceType->expects($this->any()) + ->method('canApplyMsrp') + ->with($this->equalTo($this->product)) + ->will($this->returnValue(false)); - $priceMockTwo = $this->getMockBuilder(\Magento\Framework\Pricing\Price\PriceInterface::class) - ->getMockForAbstractClass(); + $this->salableResolverMock->expects($this->once())->method('isSalable')->with($this->product)->willReturn(true); - $priceMockTwo->expects($this->once()) - ->method('getValue') - ->willReturn($finalPrice); + $result = $this->object->toHtml(); - $priceInfoMock = $this->getMockBuilder(\Magento\Framework\Pricing\PriceInfo\Base::class) + //assert price wrapper + $this->assertStringStartsWith('assertRegExp('/[final_price]/', $result); + } + + public function testNotSalableItem() + { + $this->salableResolverMock + ->expects($this->once()) + ->method('isSalable') + ->with($this->product) + ->willReturn(false); + $result = $this->object->toHtml(); + + $this->assertEmpty($result); + } + + public function testRenderMsrpEnabled() + { + $priceType = $this->getMock('Magento\Msrp\Pricing\Price\MsrpPrice', [], [], '', false); + $this->priceInfo->expects($this->once()) + ->method('getPrice') + ->with($this->equalTo('msrp_price')) + ->will($this->returnValue($priceType)); + + $priceType->expects($this->any()) + ->method('canApplyMsrp') + ->with($this->equalTo($this->product)) + ->will($this->returnValue(true)); + + $priceType->expects($this->any()) + ->method('isMinimalPriceLessMsrp') + ->with($this->equalTo($this->product)) + ->will($this->returnValue(true)); + + $priceBoxRender = $this->getMockBuilder('Magento\Framework\Pricing\Render\PriceBox') ->disableOriginalConstructor() ->getMock(); + $priceBoxRender->expects($this->once()) + ->method('toHtml') + ->will($this->returnValue('test')); + + $arguments = [ + 'real_price_html' => '', + 'zone' => 'test_zone', + ]; + $this->rendererPool->expects($this->once()) + ->method('createPriceRender') + ->with('msrp_price', $this->product, $arguments) + ->will($this->returnValue($priceBoxRender)); + + $this->salableResolverMock->expects($this->once())->method('isSalable')->with($this->product)->willReturn(true); - $priceInfoMock->expects($this->exactly(2)) + $result = $this->object->toHtml(); + + //assert price wrapper + $this->assertEquals( + '
test
', + $result + ); + } + + public function testRenderMsrpNotRegisteredException() + { + $this->logger->expects($this->once()) + ->method('critical'); + + $this->priceInfo->expects($this->once()) ->method('getPrice') - ->willReturnMap([ - [RegularPrice::PRICE_CODE, $priceMockOne], - [FinalPrice::PRICE_CODE, $priceMockTwo], - ]); + ->with($this->equalTo('msrp_price')) + ->will($this->throwException(new \InvalidArgumentException())); - $productMock = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductInterface::class) - ->setMethods(['getPriceInfo']) - ->getMockForAbstractClass(); + $this->salableResolverMock->expects($this->once())->method('isSalable')->with($this->product)->willReturn(true); - $productMock->expects($this->exactly(2)) - ->method('getPriceInfo') - ->willReturn($priceInfoMock); + $result = $this->object->toHtml(); + + //assert price wrapper + $this->assertStringStartsWith('assertRegExp('/[final_price]/', $result); + } + + public function testRenderAmountMinimal() + { + $priceType = $this->getMock('Magento\Catalog\Pricing\Price\FinalPrice', [], [], '', false); + $amount = $this->getMockForAbstractClass('Magento\Framework\Pricing\Amount\AmountInterface'); + $priceId = 'price_id'; + $html = 'html'; + $this->object->setData('price_id', $priceId); + + $arguments = [ + 'zone' => 'test_zone', + 'list_category_page' => true, + 'display_label' => 'As low as', + 'price_id' => $priceId, + 'include_container' => false, + 'skip_adjustments' => true, + ]; - $this->lowestPriceOptionsProvider->expects($this->once()) - ->method('getProducts') - ->with($this->saleableItem) - ->willReturn([$productMock]); + $amountRender = $this->getMock('Magento\Framework\Pricing\Render\Amount', ['toHtml'], [], '', false); + $amountRender->expects($this->once()) + ->method('toHtml') + ->will($this->returnValue($html)); - $this->assertEquals($expected, $this->model->hasSpecialPrice()); + $this->priceInfo->expects($this->once()) + ->method('getPrice') + ->with(\Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE) + ->will($this->returnValue($priceType)); + + $priceType->expects($this->once()) + ->method('getMinimalPrice') + ->will($this->returnValue($amount)); + + $this->rendererPool->expects($this->once()) + ->method('createAmountRender') + ->with($amount, $this->product, $this->price, $arguments) + ->will($this->returnValue($amountRender)); + + $this->assertEquals($html, $this->object->renderAmountMinimal()); } /** - * @return array + * @dataProvider hasSpecialPriceProvider + * @param float $regularPrice + * @param float $finalPrice + * @param bool $expectedResult */ - public function hasSpecialPriceDataProvider() + public function testHasSpecialPrice($regularPrice, $finalPrice, $expectedResult) + { + $regularPriceType = $this->getMock('Magento\Catalog\Pricing\Price\RegularPrice', [], [], '', false); + $finalPriceType = $this->getMock('Magento\Catalog\Pricing\Price\FinalPrice', [], [], '', false); + $regularPriceAmount = $this->getMockForAbstractClass('Magento\Framework\Pricing\Amount\AmountInterface'); + $finalPriceAmount = $this->getMockForAbstractClass('Magento\Framework\Pricing\Amount\AmountInterface'); + + $regularPriceAmount->expects($this->once()) + ->method('getValue') + ->will($this->returnValue($regularPrice)); + $finalPriceAmount->expects($this->once()) + ->method('getValue') + ->will($this->returnValue($finalPrice)); + + $regularPriceType->expects($this->once()) + ->method('getAmount') + ->will($this->returnValue($regularPriceAmount)); + $finalPriceType->expects($this->once()) + ->method('getAmount') + ->will($this->returnValue($finalPriceAmount)); + + $this->priceInfo->expects($this->at(0)) + ->method('getPrice') + ->with(\Magento\Catalog\Pricing\Price\RegularPrice::PRICE_CODE) + ->will($this->returnValue($regularPriceType)); + $this->priceInfo->expects($this->at(1)) + ->method('getPrice') + ->with(\Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE) + ->will($this->returnValue($finalPriceType)); + + $this->assertEquals($expectedResult, $this->object->hasSpecialPrice()); + } + + public function hasSpecialPriceProvider() { return [ - [10., 20., false], - [10., 10., false], - [20., 10., true], + [10.0, 20.0, false], + [20.0, 10.0, true], + [10.0, 10.0, false] ]; } + + public function testShowMinimalPrice() + { + $finalPrice = 10.0; + $minimalPrice = 5.0; + $displayMininmalPrice = 2.0; + + $this->object->setDisplayMinimalPrice($displayMininmalPrice); + + $finalPriceType = $this->getMock('Magento\Catalog\Pricing\Price\FinalPrice', [], [], '', false); + + $finalPriceAmount = $this->getMockForAbstractClass('Magento\Framework\Pricing\Amount\AmountInterface'); + $minimalPriceAmount = $this->getMockForAbstractClass('Magento\Framework\Pricing\Amount\AmountInterface'); + + $finalPriceAmount->expects($this->once()) + ->method('getValue') + ->will($this->returnValue($finalPrice)); + $minimalPriceAmount->expects($this->once()) + ->method('getValue') + ->will($this->returnValue($minimalPrice)); + + $finalPriceType->expects($this->at(0)) + ->method('getAmount') + ->will($this->returnValue($finalPriceAmount)); + $finalPriceType->expects($this->at(1)) + ->method('getMinimalPrice') + ->will($this->returnValue($minimalPriceAmount)); + + $this->priceInfo->expects($this->once()) + ->method('getPrice') + ->with(\Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE) + ->will($this->returnValue($finalPriceType)); + + $this->assertTrue($this->object->showMinimalPrice()); + } + + public function testHidePrice() + { + $this->product->expects($this->any()) + ->method('getCanShowPrice') + ->will($this->returnValue(false)); + + $this->assertEmpty($this->object->toHtml()); + } + + public function testGetCacheKey() + { + $result = $this->object->getCacheKey(); + $this->assertStringEndsWith('list-category-page', $result); + } + + public function testGetCacheKeyInfo() + { + $this->assertArrayHasKey('display_minimal_price', $this->object->getCacheKeyInfo()); + } } diff --git a/app/code/Magento/Customer/Model/Address/AbstractAddress.php b/app/code/Magento/Customer/Model/Address/AbstractAddress.php index 3ff2df1437ea4..86f2f48ab0346 100644 --- a/app/code/Magento/Customer/Model/Address/AbstractAddress.php +++ b/app/code/Magento/Customer/Model/Address/AbstractAddress.php @@ -263,7 +263,7 @@ public function setData($key, $value = null) { if (is_array($key)) { $key = $this->_implodeArrayField($key); - } elseif (is_array($value) && !empty($value) && $this->isAddressMultilineAttribute($key)) { + } elseif (is_array($value) && $this->isAddressMultilineAttribute($key)) { $value = $this->_implodeArrayValues($value); } return parent::setData($key, $value); @@ -291,7 +291,7 @@ protected function isAddressMultilineAttribute($code) protected function _implodeArrayField(array $data) { foreach ($data as $key => $value) { - if (is_array($value) && !empty($value) && $this->isAddressMultilineAttribute($key)) { + if (is_array($value) && $this->isAddressMultilineAttribute($key)) { $data[$key] = $this->_implodeArrayValues($data[$key]); } } @@ -301,22 +301,24 @@ protected function _implodeArrayField(array $data) /** * Combine values of field lines into a single string * - * @param string[]|string $value + * @param array $value * @return string */ protected function _implodeArrayValues($value) { - if (is_array($value) && count($value)) { - $isScalar = false; + if (is_array($value)) { + $isScalar = true; foreach ($value as $val) { - if (is_scalar($val)) { - $isScalar = true; + if (!is_scalar($val)) { + $isScalar = false; } } + if ($isScalar) { $value = trim(implode("\n", $value)); } } + return $value; } diff --git a/app/code/Magento/Customer/Model/Customer/DataProvider.php b/app/code/Magento/Customer/Model/Customer/DataProvider.php index 3ebe91f0ccef9..efe4592004b12 100644 --- a/app/code/Magento/Customer/Model/Customer/DataProvider.php +++ b/app/code/Magento/Customer/Model/Customer/DataProvider.php @@ -99,6 +99,8 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider ]; /** + * DataProvider Constructor + * * @param string $name * @param string $primaryFieldName * @param string $requestFieldName @@ -106,9 +108,9 @@ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider * @param CustomerCollectionFactory $customerCollectionFactory * @param Config $eavConfig * @param FilterPool $filterPool - * @param FileProcessorFactory $fileProcessorFactory * @param array $meta * @param array $data + * @param FileProcessorFactory|null $fileProcessorFactory * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -119,9 +121,9 @@ public function __construct( CustomerCollectionFactory $customerCollectionFactory, Config $eavConfig, FilterPool $filterPool, - FileProcessorFactory $fileProcessorFactory = null, array $meta = [], - array $data = [] + array $data = [], + FileProcessorFactory $fileProcessorFactory = null ) { parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data); $this->eavValidationRules = $eavValidationRules; @@ -129,13 +131,15 @@ public function __construct( $this->collection->addAttributeToSelect('*'); $this->eavConfig = $eavConfig; $this->filterPool = $filterPool; - $this->fileProcessorFactory = $fileProcessorFactory ?: $this->getFileProcessorFactory(); $this->meta['customer']['fields'] = $this->getAttributesMeta( $this->eavConfig->getEntityType('customer') ); $this->meta['address']['fields'] = $this->getAttributesMeta( $this->eavConfig->getEntityType('customer_address') ); + $this->fileProcessorFactory = $fileProcessorFactory ?: ObjectManager::getInstance()->get( + FileProcessorFactory::class + ); } /** diff --git a/app/code/Magento/Customer/Model/ResourceModel/Grid/Collection.php b/app/code/Magento/Customer/Model/ResourceModel/Grid/Collection.php new file mode 100644 index 0000000000000..e46de77b8e767 --- /dev/null +++ b/app/code/Magento/Customer/Model/ResourceModel/Grid/Collection.php @@ -0,0 +1,15 @@ +EavVersionControlSnapshot - + customer_grid_flat Magento\Customer\Model\ResourceModel\Customer - + diff --git a/app/code/Magento/Deploy/Model/Deployer.php b/app/code/Magento/Deploy/Model/Deployer.php index 9c1781dcb08ad..9e7241c9ce55b 100644 --- a/app/code/Magento/Deploy/Model/Deployer.php +++ b/app/code/Magento/Deploy/Model/Deployer.php @@ -50,7 +50,8 @@ class Deployer * @param Version\StorageInterface $versionStorage * @param JsTranslationConfig $jsTranslationConfig * @param AlternativeSourceInterface[] $alternativeSources - * @param DeployManagerFactory $deployManagerFactory + * @param bool $isDryRun + * @param DeployManagerFactory|null $deployManagerFactory * @param array $options * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ @@ -60,11 +61,14 @@ public function __construct( Version\StorageInterface $versionStorage, JsTranslationConfig $jsTranslationConfig, array $alternativeSources, + $isDryRun = false, DeployManagerFactory $deployManagerFactory = null, $options = [] ) { $this->output = $output; - $this->deployManagerFactory = $deployManagerFactory; + $this->deployManagerFactory = $deployManagerFactory ?: ObjectManager::getInstance()->get( + DeployManagerFactory::class + ); if (is_array($options)) { $this->options = $options; } else { diff --git a/app/code/Magento/Email/composer.json b/app/code/Magento/Email/composer.json index 009c8f3d3d564..ec391c7ec65c6 100644 --- a/app/code/Magento/Email/composer.json +++ b/app/code/Magento/Email/composer.json @@ -11,7 +11,7 @@ "magento/framework": "100.0.*" }, "type": "magento2-module", - "version": "100.0.5", + "version": "100.0.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/ImportExport/composer.json b/app/code/Magento/ImportExport/composer.json index 470f28dca937a..0bc4391ee4e88 100644 --- a/app/code/Magento/ImportExport/composer.json +++ b/app/code/Magento/ImportExport/composer.json @@ -11,7 +11,7 @@ "ext-ctype": "*" }, "type": "magento2-module", - "version": "100.0.5", + "version": "100.0.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Indexer/composer.json b/app/code/Magento/Indexer/composer.json index e3a75d52483dc..c357cce991d25 100644 --- a/app/code/Magento/Indexer/composer.json +++ b/app/code/Magento/Indexer/composer.json @@ -8,7 +8,7 @@ "magento/framework": "100.0.*" }, "type": "magento2-module", - "version": "100.0.5", + "version": "100.0.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Newsletter/composer.json b/app/code/Magento/Newsletter/composer.json index f6414b130ac1e..1002c9db6bdff 100644 --- a/app/code/Magento/Newsletter/composer.json +++ b/app/code/Magento/Newsletter/composer.json @@ -15,7 +15,7 @@ "magento/framework": "100.0.*" }, "type": "magento2-module", - "version": "100.0.7", + "version": "100.0.8", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/PageCache/composer.json b/app/code/Magento/PageCache/composer.json index 303c07a7d4c98..962c5bef0e641 100644 --- a/app/code/Magento/PageCache/composer.json +++ b/app/code/Magento/PageCache/composer.json @@ -9,7 +9,7 @@ "magento/framework": "100.0.*" }, "type": "magento2-module", - "version": "100.0.6", + "version": "100.0.7", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/payflowpro-method.js b/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/payflowpro-method.js index 5f5695b2bad5d..cd53ddcf49148 100644 --- a/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/payflowpro-method.js +++ b/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/payflowpro-method.js @@ -52,18 +52,24 @@ define( var self = this; if (this.validateHandler() && additionalValidators.validate()) { - fullScreenLoader.startLoader(); this.isPlaceOrderActionAllowed(false); - $.when(setPaymentInformationAction(this.messageContainer, { - 'method': self.getCode() - })).done(function () { - self.placeOrderHandler().fail(function () { + fullScreenLoader.startLoader(); + $.when( + setPaymentInformationAction(this.messageContainer, {'method': self.getCode()}) + ).done( + function () { + self.placeOrderHandler().fail( + function () { + fullScreenLoader.stopLoader(); + } + ); + } + ).always( + function () { + self.isPlaceOrderActionAllowed(true); fullScreenLoader.stopLoader(); - }); - }).fail(function () { - fullScreenLoader.stopLoader(); - self.isPlaceOrderActionAllowed(true); - }); + } + ); } } }); diff --git a/app/code/Magento/Quote/Model/QuoteManagement.php b/app/code/Magento/Quote/Model/QuoteManagement.php index dcec33d999e32..4292d379e3f35 100644 --- a/app/code/Magento/Quote/Model/QuoteManagement.php +++ b/app/code/Magento/Quote/Model/QuoteManagement.php @@ -354,7 +354,7 @@ public function placeOrder($cartId, PaymentInterface $paymentMethod = null) $order = $this->submit($quote); if (null == $order) { - throw new LocalizedException(__('Cannot place order.')); + throw new LocalizedException(__('Unable to place order. Please try again later.')); } $this->checkoutSession->setLastQuoteId($quote->getId()); diff --git a/app/code/Magento/Quote/i18n/en_US.csv b/app/code/Magento/Quote/i18n/en_US.csv index 9e7925f16e2e2..cd6aa292c2128 100644 --- a/app/code/Magento/Quote/i18n/en_US.csv +++ b/app/code/Magento/Quote/i18n/en_US.csv @@ -29,3 +29,4 @@ Shipping,Shipping "Please specify a shipping method.","Please specify a shipping method." "Please check the billing address information. %1","Please check the billing address information. %1" "Please select a valid payment method.","Please select a valid payment method." +"Unable to place order. Please try again later.","Unable to place order. Please try again later." diff --git a/app/code/Magento/Sales/Api/Data/CreditmemoCommentCreationInterface.php b/app/code/Magento/Sales/Api/Data/CreditmemoCommentCreationInterface.php index 283e8600738e7..47fab560ba8a7 100644 --- a/app/code/Magento/Sales/Api/Data/CreditmemoCommentCreationInterface.php +++ b/app/code/Magento/Sales/Api/Data/CreditmemoCommentCreationInterface.php @@ -9,8 +9,6 @@ /** * Interface CreditmemoCommentCreationInterface - * - * @api */ interface CreditmemoCommentCreationInterface extends ExtensibleDataInterface, CommentInterface { diff --git a/app/code/Magento/Sales/Api/Data/CreditmemoCreationArgumentsInterface.php b/app/code/Magento/Sales/Api/Data/CreditmemoCreationArgumentsInterface.php index 2735a94a777f0..4b5f30928f8fa 100644 --- a/app/code/Magento/Sales/Api/Data/CreditmemoCreationArgumentsInterface.php +++ b/app/code/Magento/Sales/Api/Data/CreditmemoCreationArgumentsInterface.php @@ -7,8 +7,6 @@ /** * Interface CreditmemoCreationArgumentsInterface - * - * @api */ interface CreditmemoCreationArgumentsInterface { diff --git a/app/code/Magento/Sales/Api/Data/CreditmemoItemCreationInterface.php b/app/code/Magento/Sales/Api/Data/CreditmemoItemCreationInterface.php index 1cd5f3c43be33..6ef5016582d58 100644 --- a/app/code/Magento/Sales/Api/Data/CreditmemoItemCreationInterface.php +++ b/app/code/Magento/Sales/Api/Data/CreditmemoItemCreationInterface.php @@ -9,7 +9,6 @@ /** * Interface CreditmemoItemCreationInterface - * @api */ interface CreditmemoItemCreationInterface extends LineItemInterface, ExtensibleDataInterface { diff --git a/app/code/Magento/Sales/Api/Exception/CouldNotRefundExceptionInterface.php b/app/code/Magento/Sales/Api/Exception/CouldNotRefundExceptionInterface.php index dee24735af620..6b7cf9e121d58 100644 --- a/app/code/Magento/Sales/Api/Exception/CouldNotRefundExceptionInterface.php +++ b/app/code/Magento/Sales/Api/Exception/CouldNotRefundExceptionInterface.php @@ -6,7 +6,7 @@ namespace Magento\Sales\Api\Exception; /** - * @api + * Interface for CouldNotRefund Exception. */ interface CouldNotRefundExceptionInterface { diff --git a/app/code/Magento/Sales/Api/RefundInvoiceInterface.php b/app/code/Magento/Sales/Api/RefundInvoiceInterface.php index dc86ccbb58006..2baea09be086c 100644 --- a/app/code/Magento/Sales/Api/RefundInvoiceInterface.php +++ b/app/code/Magento/Sales/Api/RefundInvoiceInterface.php @@ -7,8 +7,6 @@ /** * Interface RefundInvoiceInterface - * - * @api */ interface RefundInvoiceInterface { diff --git a/app/code/Magento/Sales/Api/RefundOrderInterface.php b/app/code/Magento/Sales/Api/RefundOrderInterface.php index 20a782de42198..72ac32c49f18a 100644 --- a/app/code/Magento/Sales/Api/RefundOrderInterface.php +++ b/app/code/Magento/Sales/Api/RefundOrderInterface.php @@ -7,8 +7,6 @@ /** * Interface RefundOrderInterface - * - * @api */ interface RefundOrderInterface { diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/CreditmemoLoader.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/CreditmemoLoader.php index 36f1310079ed5..ac3ffdc3d622c 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/CreditmemoLoader.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/CreditmemoLoader.php @@ -217,9 +217,9 @@ public function load() foreach ($creditmemo->getAllItems() as $creditmemoItem) { $orderItem = $creditmemoItem->getOrderItem(); $parentId = $orderItem->getParentItemId(); - if (isset($backToStock[$orderItem->getId()])) { + if ($parentId && isset($backToStock[$parentId]) && $backToStock[$parentId]) { $creditmemoItem->setBackToStock(true); - } elseif ($orderItem->getParentItem() && isset($backToStock[$parentId]) && $backToStock[$parentId]) { + } elseif (isset($backToStock[$orderItem->getId()])) { $creditmemoItem->setBackToStock(true); } elseif (empty($savedData)) { $creditmemoItem->setBackToStock( diff --git a/app/code/Magento/Sales/Model/InvoiceOrder.php b/app/code/Magento/Sales/Model/InvoiceOrder.php index c503b01a5ab21..3cf7577ecc958 100644 --- a/app/code/Magento/Sales/Model/InvoiceOrder.php +++ b/app/code/Magento/Sales/Model/InvoiceOrder.php @@ -12,13 +12,16 @@ use Magento\Sales\Api\InvoiceOrderInterface; use Magento\Sales\Api\OrderRepositoryInterface; use Magento\Sales\Model\Order\Config as OrderConfig; +use Magento\Sales\Model\Order\Invoice\InvoiceValidatorInterface; use Magento\Sales\Model\Order\Invoice\NotifierInterface; use Magento\Sales\Model\Order\InvoiceDocumentFactory; use Magento\Sales\Model\Order\InvoiceRepository; use Magento\Sales\Model\Order\OrderStateResolverInterface; +use Magento\Sales\Model\Order\OrderValidatorInterface; use Magento\Sales\Model\Order\PaymentAdapterInterface; use Magento\Sales\Model\Order\Validation\InvoiceOrderInterface as InvoiceOrderValidator; use Psr\Log\LoggerInterface; +use Magento\Framework\App\ObjectManager; /** * Class InvoiceOrder @@ -81,26 +84,31 @@ class InvoiceOrder implements InvoiceOrderInterface * @param ResourceConnection $resourceConnection * @param OrderRepositoryInterface $orderRepository * @param InvoiceDocumentFactory $invoiceDocumentFactory + * @param InvoiceValidatorInterface $invoiceValidator + * @param OrderValidatorInterface $orderValidator * @param PaymentAdapterInterface $paymentAdapter * @param OrderStateResolverInterface $orderStateResolver * @param OrderConfig $config * @param InvoiceRepository $invoiceRepository - * @param InvoiceOrderValidator $invoiceOrderValidator * @param NotifierInterface $notifierInterface * @param LoggerInterface $logger + * @param InvoiceOrderValidator|null $invoiceOrderValidator * @SuppressWarnings(PHPMD.ExcessiveParameterList) + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( ResourceConnection $resourceConnection, OrderRepositoryInterface $orderRepository, InvoiceDocumentFactory $invoiceDocumentFactory, + InvoiceValidatorInterface $invoiceValidator, + OrderValidatorInterface $orderValidator, PaymentAdapterInterface $paymentAdapter, OrderStateResolverInterface $orderStateResolver, OrderConfig $config, InvoiceRepository $invoiceRepository, - InvoiceOrderValidator $invoiceOrderValidator, NotifierInterface $notifierInterface, - LoggerInterface $logger + LoggerInterface $logger, + InvoiceOrderValidator $invoiceOrderValidator = null ) { $this->resourceConnection = $resourceConnection; $this->orderRepository = $orderRepository; @@ -109,9 +117,11 @@ public function __construct( $this->orderStateResolver = $orderStateResolver; $this->config = $config; $this->invoiceRepository = $invoiceRepository; - $this->invoiceOrderValidator = $invoiceOrderValidator; $this->notifierInterface = $notifierInterface; $this->logger = $logger; + $this->invoiceOrderValidator = $invoiceOrderValidator ?: ObjectManager::getInstance()->get( + InvoiceOrderValidator::class + ); } /** diff --git a/app/code/Magento/Sales/Model/Order/Creditmemo/Notifier.php b/app/code/Magento/Sales/Model/Order/Creditmemo/Notifier.php index 47dbecca6b59b..a7b737f0b508a 100644 --- a/app/code/Magento/Sales/Model/Order/Creditmemo/Notifier.php +++ b/app/code/Magento/Sales/Model/Order/Creditmemo/Notifier.php @@ -7,8 +7,6 @@ /** * CreditMemo notifier. - * - * @api */ class Notifier implements \Magento\Sales\Model\Order\Creditmemo\NotifierInterface { diff --git a/app/code/Magento/Sales/Model/Order/Creditmemo/NotifierInterface.php b/app/code/Magento/Sales/Model/Order/Creditmemo/NotifierInterface.php index ef42bd18633cf..5be7fa2d2a290 100644 --- a/app/code/Magento/Sales/Model/Order/Creditmemo/NotifierInterface.php +++ b/app/code/Magento/Sales/Model/Order/Creditmemo/NotifierInterface.php @@ -7,8 +7,6 @@ /** * Interface for CreditMemo notifier. - * - * @api */ interface NotifierInterface { diff --git a/app/code/Magento/Sales/Model/Order/CreditmemoDocumentFactory.php b/app/code/Magento/Sales/Model/Order/CreditmemoDocumentFactory.php index 26271135328c0..4c07ee8888702 100644 --- a/app/code/Magento/Sales/Model/Order/CreditmemoDocumentFactory.php +++ b/app/code/Magento/Sales/Model/Order/CreditmemoDocumentFactory.php @@ -7,8 +7,6 @@ /** * Class CreditmemoDocumentFactory - * - * @api */ class CreditmemoDocumentFactory { diff --git a/app/code/Magento/Sales/Model/Order/CreditmemoFactory.php b/app/code/Magento/Sales/Model/Order/CreditmemoFactory.php index 21104933a51d6..ff687074e4a66 100644 --- a/app/code/Magento/Sales/Model/Order/CreditmemoFactory.php +++ b/app/code/Magento/Sales/Model/Order/CreditmemoFactory.php @@ -3,7 +3,6 @@ * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Sales\Model\Order; /** @@ -23,6 +22,11 @@ class CreditmemoFactory */ protected $taxConfig; + /** + * @var \Magento\Framework\Unserialize\Unserialize + */ + protected $unserialize; + /** * Factory constructor * @@ -57,7 +61,12 @@ public function createByOrder(\Magento\Sales\Model\Order $order, array $data = [ $item = $this->convertor->itemToCreditmemoItem($orderItem); if ($orderItem->isDummy()) { - $qty = 1; + if (isset($data['qtys'][$orderItem->getParentItemId()])) { + $parentQty = $data['qtys'][$orderItem->getParentItemId()]; + } else { + $parentQty = $orderItem->getParentItem() ? $orderItem->getParentItem()->getQtyToRefund() : 1; + } + $qty = $this->calculateProductOptions($orderItem, $parentQty); $orderItem->setLockedDoShip(true); } else { if (isset($qtys[$orderItem->getId()])) { @@ -132,7 +141,12 @@ public function createByInvoice(\Magento\Sales\Model\Order\Invoice $invoice, arr $item = $this->convertor->itemToCreditmemoItem($orderItem); if ($orderItem->isDummy()) { - $qty = 1; + if (isset($data['qtys'][$orderItem->getParentItemId()])) { + $parentQty = $data['qtys'][$orderItem->getParentItemId()]; + } else { + $parentQty = $orderItem->getParentItem() ? $orderItem->getParentItem()->getQtyToRefund() : 1; + } + $qty = $this->calculateProductOptions($orderItem, $parentQty); } else { if (isset($qtys[$orderItem->getId()])) { $qty = (double)$qtys[$orderItem->getId()]; @@ -245,4 +259,38 @@ protected function initData($creditmemo, $data) $creditmemo->setAdjustmentNegative($data['adjustment_negative']); } } + + /** + * @param \Magento\Sales\Api\Data\OrderItemInterface $orderItem + * @param array $qtys + * @return int + */ + private function calculateProductOptions(\Magento\Sales\Api\Data\OrderItemInterface $orderItem, $parentQty) + { + $qty = $parentQty; + $productOptions = $orderItem->getProductOptions(); + if (isset($productOptions['bundle_selection_attributes'])) { + $bundleSelectionAttributes = $this->getUnserialize() + ->unserialize($productOptions['bundle_selection_attributes']); + if ($bundleSelectionAttributes) { + $qty = $bundleSelectionAttributes['qty'] * $parentQty; + } + } + return $qty; + } + + /** + * Get Unserialize + * + * @return \Magento\Framework\Unserialize\Unserialize + * @deprecated + */ + private function getUnserialize() + { + if (!$this->unserialize) { + $this->unserialize = \Magento\Framework\App\ObjectManager::getInstance() + ->get(\Magento\Framework\Unserialize\Unserialize::class); + } + return $this->unserialize; + } } diff --git a/app/code/Magento/Sales/Model/Order/RefundAdapterInterface.php b/app/code/Magento/Sales/Model/Order/RefundAdapterInterface.php index afcbb1c773d17..cf340d3276ca5 100644 --- a/app/code/Magento/Sales/Model/Order/RefundAdapterInterface.php +++ b/app/code/Magento/Sales/Model/Order/RefundAdapterInterface.php @@ -7,8 +7,6 @@ /** * Interface RefundAdapterInterface - * - * @api */ interface RefundAdapterInterface { diff --git a/app/code/Magento/Sales/Model/Order/Validation/InvoiceOrderInterface.php b/app/code/Magento/Sales/Model/Order/Validation/InvoiceOrderInterface.php index 5c27741a19855..374ca54e30368 100644 --- a/app/code/Magento/Sales/Model/Order/Validation/InvoiceOrderInterface.php +++ b/app/code/Magento/Sales/Model/Order/Validation/InvoiceOrderInterface.php @@ -13,8 +13,6 @@ /** * Interface InvoiceOrderInterface - * - * @api */ interface InvoiceOrderInterface { diff --git a/app/code/Magento/Sales/Model/Order/Validation/RefundInvoiceInterface.php b/app/code/Magento/Sales/Model/Order/Validation/RefundInvoiceInterface.php index 83acc9811bb89..3536a07e0fdcb 100644 --- a/app/code/Magento/Sales/Model/Order/Validation/RefundInvoiceInterface.php +++ b/app/code/Magento/Sales/Model/Order/Validation/RefundInvoiceInterface.php @@ -12,8 +12,6 @@ /** * Interface RefundInvoiceInterface - * - * @api */ interface RefundInvoiceInterface { diff --git a/app/code/Magento/Sales/Model/Order/Validation/RefundOrderInterface.php b/app/code/Magento/Sales/Model/Order/Validation/RefundOrderInterface.php index 2f770d20b5134..5c4584edee0e2 100644 --- a/app/code/Magento/Sales/Model/Order/Validation/RefundOrderInterface.php +++ b/app/code/Magento/Sales/Model/Order/Validation/RefundOrderInterface.php @@ -11,8 +11,6 @@ /** * Interface RefundOrderInterface - * - * @api */ interface RefundOrderInterface { diff --git a/app/code/Magento/Sales/Model/Order/Validation/ShipOrderInterface.php b/app/code/Magento/Sales/Model/Order/Validation/ShipOrderInterface.php index 43f12df445b79..3014817a5e499 100644 --- a/app/code/Magento/Sales/Model/Order/Validation/ShipOrderInterface.php +++ b/app/code/Magento/Sales/Model/Order/Validation/ShipOrderInterface.php @@ -10,8 +10,6 @@ /** * Interface ShipOrderInterface - * - * @api */ interface ShipOrderInterface { diff --git a/app/code/Magento/Sales/Model/ResourceModel/Order/Item/Collection.php b/app/code/Magento/Sales/Model/ResourceModel/Order/Item/Collection.php index 9fa5c8ae13e28..88725250d4fb5 100644 --- a/app/code/Magento/Sales/Model/ResourceModel/Order/Item/Collection.php +++ b/app/code/Magento/Sales/Model/ResourceModel/Order/Item/Collection.php @@ -55,7 +55,6 @@ protected function _afterLoad() * Assign parent items */ foreach ($this as $item) { - $this->_resource->unserializeFields($item); if ($item->getParentItemId()) { $item->setParentItem($this->getItemById($item->getParentItemId())); } diff --git a/app/code/Magento/Sales/Model/ShipOrder.php b/app/code/Magento/Sales/Model/ShipOrder.php index 034442a19c1f7..ebc42c3f9c891 100644 --- a/app/code/Magento/Sales/Model/ShipOrder.php +++ b/app/code/Magento/Sales/Model/ShipOrder.php @@ -11,11 +11,14 @@ use Magento\Sales\Api\ShipOrderInterface; use Magento\Sales\Model\Order\Config as OrderConfig; use Magento\Sales\Model\Order\OrderStateResolverInterface; +use Magento\Sales\Model\Order\OrderValidatorInterface; use Magento\Sales\Model\Order\ShipmentDocumentFactory; use Magento\Sales\Model\Order\Shipment\NotifierInterface; +use Magento\Sales\Model\Order\Shipment\ShipmentValidatorInterface; use Magento\Sales\Model\Order\Shipment\OrderRegistrarInterface; use Magento\Sales\Model\Order\Validation\ShipOrderInterface as ShipOrderValidator; use Psr\Log\LoggerInterface; +use Magento\Framework\App\ObjectManager; /** * Class ShipOrder @@ -77,37 +80,46 @@ class ShipOrder implements ShipOrderInterface * @param ResourceConnection $resourceConnection * @param OrderRepositoryInterface $orderRepository * @param ShipmentDocumentFactory $shipmentDocumentFactory + * @param ShipmentValidatorInterface $shipmentValidator + * @param OrderValidatorInterface $orderValidator * @param OrderStateResolverInterface $orderStateResolver * @param OrderConfig $config * @param ShipmentRepositoryInterface $shipmentRepository - * @param ShipOrderValidator $shipOrderValidator * @param NotifierInterface $notifierInterface * @param OrderRegistrarInterface $orderRegistrar - * @param LoggerInterface $logger + * @param LoggerInterface $logger, + * @param ShipOrderValidator|null $shipOrderValidator * @SuppressWarnings(PHPMD.ExcessiveParameterList) + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function __construct( ResourceConnection $resourceConnection, OrderRepositoryInterface $orderRepository, ShipmentDocumentFactory $shipmentDocumentFactory, + ShipmentValidatorInterface $shipmentValidator, + OrderValidatorInterface $orderValidator, OrderStateResolverInterface $orderStateResolver, OrderConfig $config, ShipmentRepositoryInterface $shipmentRepository, - ShipOrderValidator $shipOrderValidator, NotifierInterface $notifierInterface, OrderRegistrarInterface $orderRegistrar, - LoggerInterface $logger + LoggerInterface $logger, + ShipOrderValidator $shipOrderValidator = null ) { $this->resourceConnection = $resourceConnection; $this->orderRepository = $orderRepository; $this->shipmentDocumentFactory = $shipmentDocumentFactory; + $this->shipmentValidator = $shipmentValidator; + $this->orderValidator = $orderValidator; $this->orderStateResolver = $orderStateResolver; $this->config = $config; $this->shipmentRepository = $shipmentRepository; - $this->shipOrderValidator = $shipOrderValidator; $this->notifierInterface = $notifierInterface; $this->logger = $logger; $this->orderRegistrar = $orderRegistrar; + $this->shipOrderValidator = $shipOrderValidator ?: ObjectManager::getInstance()->get( + ShipOrderValidator::class + ); } /** diff --git a/app/code/Magento/Sales/Model/Validator.php b/app/code/Magento/Sales/Model/Validator.php index 281d517bdc2e5..e23aca1901a8d 100644 --- a/app/code/Magento/Sales/Model/Validator.php +++ b/app/code/Magento/Sales/Model/Validator.php @@ -7,6 +7,7 @@ use Magento\Framework\Exception\LocalizedException; use Magento\Framework\ObjectManagerInterface; +use Magento\Framework\App\ObjectManager; /** * Class Validator @@ -29,14 +30,16 @@ class Validator * Validator constructor. * * @param ObjectManagerInterface $objectManager - * @param ValidatorResultInterfaceFactory $validatorResult + * @param ValidatorResultInterfaceFactory|null $validatorResult */ public function __construct( ObjectManagerInterface $objectManager, - ValidatorResultInterfaceFactory $validatorResult + ValidatorResultInterfaceFactory $validatorResult = null ) { $this->objectManager = $objectManager; - $this->validatorResultFactory = $validatorResult; + $this->validatorResultFactory = $validatorResult ?: ObjectManager::getInstance()->get( + ValidatorResultInterfaceFactory::class + ); } /** diff --git a/app/code/Magento/Sales/Model/ValidatorResultInterface.php b/app/code/Magento/Sales/Model/ValidatorResultInterface.php index c072d30e2f93f..727f28263822e 100644 --- a/app/code/Magento/Sales/Model/ValidatorResultInterface.php +++ b/app/code/Magento/Sales/Model/ValidatorResultInterface.php @@ -7,7 +7,6 @@ /** * Interface ValidatorResultInterface - * @api */ interface ValidatorResultInterface { diff --git a/app/code/Magento/Sales/Test/Unit/Model/InvoiceOrderTest.php b/app/code/Magento/Sales/Test/Unit/Model/InvoiceOrderTest.php index 1169e230e7542..36368a5c83867 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/InvoiceOrderTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/InvoiceOrderTest.php @@ -23,6 +23,7 @@ use Magento\Sales\Model\ValidatorResultInterface; use Magento\Sales\Model\InvoiceOrder; use Psr\Log\LoggerInterface; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; /** * Class InvoiceOrderTest @@ -119,6 +120,8 @@ class InvoiceOrderTest extends \PHPUnit_Framework_TestCase protected function setUp() { + $objectManager = new ObjectManager($this); + $this->resourceConnectionMock = $this->getMockBuilder(ResourceConnection::class) ->disableOriginalConstructor() ->getMock(); @@ -184,17 +187,20 @@ protected function setUp() ->setMethods(['hasMessages', 'getMessages', 'addMessage']) ->getMock(); - $this->invoiceOrder = new InvoiceOrder( - $this->resourceConnectionMock, - $this->orderRepositoryMock, - $this->invoiceDocumentFactoryMock, - $this->paymentAdapterMock, - $this->orderStateResolverMock, - $this->configMock, - $this->invoiceRepositoryMock, - $this->invoiceOrderValidatorMock, - $this->notifierInterfaceMock, - $this->loggerMock + $this->invoiceOrder = $objectManager->getObject( + InvoiceOrder::class, + [ + 'resourceConnection' => $this->resourceConnectionMock, + 'orderRepository' => $this->orderRepositoryMock, + 'invoiceDocumentFactory' => $this->invoiceDocumentFactoryMock, + 'paymentAdapter' => $this->paymentAdapterMock, + 'orderStateResolver' => $this->orderStateResolverMock, + 'config' => $this->configMock, + 'invoiceRepository' => $this->invoiceRepositoryMock, + 'invoiceOrderValidator' => $this->invoiceOrderValidatorMock, + 'notifierInterface' => $this->notifierInterfaceMock, + 'logger' => $this->loggerMock + ] ); } diff --git a/app/code/Magento/SalesInventory/Model/Order/ReturnProcessor.php b/app/code/Magento/SalesInventory/Model/Order/ReturnProcessor.php index 7752d7131031c..3680bbb3a1eae 100644 --- a/app/code/Magento/SalesInventory/Model/Order/ReturnProcessor.php +++ b/app/code/Magento/SalesInventory/Model/Order/ReturnProcessor.php @@ -6,7 +6,6 @@ namespace Magento\SalesInventory\Model\Order; use Magento\Sales\Api\Data\CreditmemoInterface; -use Magento\Sales\Api\Data\CreditmemoItemInterface; use Magento\Sales\Api\Data\OrderInterface; /** @@ -29,52 +28,35 @@ class ReturnProcessor */ private $priceIndexer; - /** - * @var \Magento\Sales\Api\CreditmemoRepositoryInterface - */ - private $creditmemoRepository; - /** * @var \Magento\Store\Model\StoreManagerInterface */ private $storeManager; - /** - * @var \Magento\Sales\Api\OrderRepositoryInterface - */ - private $orderRepository; - /** * @var \Magento\Sales\Api\OrderItemRepositoryInterface */ private $orderItemRepository; /** - * ReturnToStockPlugin constructor. - * @param \Magento\CatalogInventory\Api\StockConfigurationInterface $stockConfiguration + * ReturnProcessor constructor. * @param \Magento\CatalogInventory\Api\StockManagementInterface $stockManagement * @param \Magento\CatalogInventory\Model\Indexer\Stock\Processor $stockIndexer * @param \Magento\Catalog\Model\Indexer\Product\Price\Processor $priceIndexer - * @param \Magento\Sales\Api\CreditmemoRepositoryInterface $creditmemoRepository * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository * @param \Magento\Sales\Api\OrderItemRepositoryInterface $orderItemRepository */ public function __construct( \Magento\CatalogInventory\Api\StockManagementInterface $stockManagement, \Magento\CatalogInventory\Model\Indexer\Stock\Processor $stockIndexer, \Magento\Catalog\Model\Indexer\Product\Price\Processor $priceIndexer, - \Magento\Sales\Api\CreditmemoRepositoryInterface $creditmemoRepository, \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Sales\Api\OrderRepositoryInterface $orderRepository, \Magento\Sales\Api\OrderItemRepositoryInterface $orderItemRepository ) { $this->stockManagement = $stockManagement; $this->stockIndexerProcessor = $stockIndexer; $this->priceIndexer = $priceIndexer; - $this->creditmemoRepository = $creditmemoRepository; $this->storeManager = $storeManager; - $this->orderRepository = $orderRepository; $this->orderItemRepository = $orderItemRepository; } @@ -82,22 +64,22 @@ public function __construct( * @param CreditmemoInterface $creditmemo * @param OrderInterface $order * @param array $returnToStockItems + * @param bool $isAutoReturn * @return void */ public function execute( CreditmemoInterface $creditmemo, OrderInterface $order, - array $returnToStockItems = [] + array $returnToStockItems = [], + $isAutoReturn = false ) { $itemsToUpdate = []; foreach ($creditmemo->getItems() as $item) { - $qty = $item->getQty(); $productId = $item->getProductId(); $orderItem = $this->orderItemRepository->get($item->getOrderItemId()); $parentItemId = $orderItem->getParentItemId(); - if ($this->canReturnItem($item, $qty, $parentItemId, $returnToStockItems)) { - $parentItem = $parentItemId ? $this->getItemByOrderId($creditmemo, $parentItemId) : false; - $qty = $parentItem ? $parentItem->getQty() * $qty : $qty; + $qty = $item->getQty(); + if ($isAutoReturn || $this->canReturnItem($item, $qty, $parentItemId, $returnToStockItems)) { if (isset($itemsToUpdate[$productId])) { $itemsToUpdate[$productId] += $qty; } else { @@ -122,21 +104,6 @@ public function execute( } } - /** - * @param \Magento\Sales\Api\Data\CreditmemoInterface $creditmemo - * @param int $parentItemId - * @return bool|CreditmemoItemInterface - */ - private function getItemByOrderId(\Magento\Sales\Api\Data\CreditmemoInterface $creditmemo, $parentItemId) - { - foreach ($creditmemo->getItems() as $item) { - if ($item->getOrderItemId() == $parentItemId) { - return $item; - } - } - return false; - } - /** * @param \Magento\Sales\Api\Data\CreditmemoItemInterface $item * @param int $qty diff --git a/app/code/Magento/CatalogInventory/Observer/RefundOrderInventoryObserver.php b/app/code/Magento/SalesInventory/Observer/RefundOrderInventoryObserver.php similarity index 57% rename from app/code/Magento/CatalogInventory/Observer/RefundOrderInventoryObserver.php rename to app/code/Magento/SalesInventory/Observer/RefundOrderInventoryObserver.php index 9702bfc7cfe42..acdebcf976a2e 100644 --- a/app/code/Magento/CatalogInventory/Observer/RefundOrderInventoryObserver.php +++ b/app/code/Magento/SalesInventory/Observer/RefundOrderInventoryObserver.php @@ -4,54 +4,74 @@ * See COPYING.txt for license details. */ -namespace Magento\CatalogInventory\Observer; +namespace Magento\SalesInventory\Observer; use Magento\CatalogInventory\Api\StockConfigurationInterface; use Magento\CatalogInventory\Api\StockManagementInterface; use Magento\Framework\Event\Observer as EventObserver; use Magento\Framework\Event\ObserverInterface; +use Magento\Sales\Model\OrderRepository; +use Magento\SalesInventory\Model\Order\ReturnProcessor; /** * Catalog inventory module observer + * @deprecated */ class RefundOrderInventoryObserver implements ObserverInterface { /** * @var StockConfigurationInterface */ - protected $stockConfiguration; + private $stockConfiguration; /** * @var StockManagementInterface */ - protected $stockManagement; + private $stockManagement; /** * @var \Magento\CatalogInventory\Model\Indexer\Stock\Processor */ - protected $stockIndexerProcessor; + private $stockIndexerProcessor; /** * @var \Magento\Catalog\Model\Indexer\Product\Price\Processor */ - protected $priceIndexer; + private $priceIndexer; /** + * @var \Magento\SalesInventory\Model\Order\ReturnProcessor + */ + private $returnProcessor; + + /** + * @var \Magento\Sales\Api\OrderRepositoryInterface + */ + private $orderRepository; + + /** + * RefundOrderInventoryObserver constructor. * @param StockConfigurationInterface $stockConfiguration * @param StockManagementInterface $stockManagement * @param \Magento\CatalogInventory\Model\Indexer\Stock\Processor $stockIndexerProcessor * @param \Magento\Catalog\Model\Indexer\Product\Price\Processor $priceIndexer + * @param ReturnProcessor $returnProcessor + * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository */ public function __construct( StockConfigurationInterface $stockConfiguration, StockManagementInterface $stockManagement, \Magento\CatalogInventory\Model\Indexer\Stock\Processor $stockIndexerProcessor, - \Magento\Catalog\Model\Indexer\Product\Price\Processor $priceIndexer + \Magento\Catalog\Model\Indexer\Product\Price\Processor $priceIndexer, + \Magento\SalesInventory\Model\Order\ReturnProcessor $returnProcessor, + \Magento\Sales\Api\OrderRepositoryInterface $orderRepository ) { $this->stockConfiguration = $stockConfiguration; $this->stockManagement = $stockManagement; $this->stockIndexerProcessor = $stockIndexerProcessor; $this->priceIndexer = $priceIndexer; + $this->returnProcessor = $returnProcessor; + $this->orderRepository = $orderRepository; } /** @@ -64,31 +84,18 @@ public function execute(EventObserver $observer) { /* @var $creditmemo \Magento\Sales\Model\Order\Creditmemo */ $creditmemo = $observer->getEvent()->getCreditmemo(); - $itemsToUpdate = []; - foreach ($creditmemo->getAllItems() as $item) { - $qty = $item->getQty(); - if (($item->getBackToStock() && $qty) || $this->stockConfiguration->isAutoReturnEnabled()) { - $productId = $item->getProductId(); - $parentItemId = $item->getOrderItem()->getParentItemId(); - /* @var $parentItem \Magento\Sales\Model\Order\Creditmemo\Item */ - $parentItem = $parentItemId ? $creditmemo->getItemByOrderId($parentItemId) : false; - $qty = $parentItem ? $parentItem->getQty() * $qty : $qty; - if (isset($itemsToUpdate[$productId])) { - $itemsToUpdate[$productId] += $qty; - } else { - $itemsToUpdate[$productId] = $qty; - } + $order = $this->orderRepository->get($creditmemo->getOrderId()); + $returnToStockItems = []; + foreach ($creditmemo->getItems() as $item) { + if ($item->getBackToStock()) { + $returnToStockItems[] = $item->getOrderItemId(); } } - if (!empty($itemsToUpdate)) { - $this->stockManagement->revertProductsSale( - $itemsToUpdate, - $creditmemo->getStore()->getWebsiteId() - ); - - $updatedItemIds = array_keys($itemsToUpdate); - $this->stockIndexerProcessor->reindexList($updatedItemIds); - $this->priceIndexer->reindexList($updatedItemIds); - } + $this->returnProcessor->execute( + $creditmemo, + $order, + $returnToStockItems, + $this->stockConfiguration->isAutoReturnEnabled() + ); } } diff --git a/app/code/Magento/SalesInventory/Test/Unit/Model/Order/ReturnProcessorTest.php b/app/code/Magento/SalesInventory/Test/Unit/Model/Order/ReturnProcessorTest.php index 523759d54645a..efa3bff32c0fa 100644 --- a/app/code/Magento/SalesInventory/Test/Unit/Model/Order/ReturnProcessorTest.php +++ b/app/code/Magento/SalesInventory/Test/Unit/Model/Order/ReturnProcessorTest.php @@ -5,9 +5,7 @@ */ namespace Magento\SalesInventory\Test\Unit\Model\Order; -use Magento\CatalogInventory\Api\StockConfigurationInterface; use Magento\CatalogInventory\Api\StockManagementInterface; -use Magento\Sales\Api\CreditmemoRepositoryInterface; use Magento\Sales\Api\Data\CreditmemoInterface; use Magento\Sales\Api\Data\CreditmemoItemInterface; use Magento\Sales\Api\Data\OrderInterface; @@ -49,21 +47,11 @@ class ReturnProcessorTest extends \PHPUnit_Framework_TestCase */ private $priceIndexerMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject|CreditmemoRepositoryInterface - */ - private $creditmemoRepositoryMock; - /** * @var \PHPUnit_Framework_MockObject_MockObject|StoreManagerInterface */ private $storeManagerMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject|OrderRepositoryInterface - */ - private $orderRepositoryMock; - /** * @var \PHPUnit_Framework_MockObject_MockObject|OrderItemRepositoryInterface */ @@ -95,13 +83,10 @@ public function setUp() $this->priceIndexerMock = $this->getMockBuilder(\Magento\Catalog\Model\Indexer\Product\Price\Processor::class) ->disableOriginalConstructor() ->getMock(); - $this->creditmemoRepositoryMock = $this->getMockBuilder(CreditmemoRepositoryInterface::class) - ->disableOriginalConstructor() - ->getMock(); $this->storeManagerMock = $this->getMockBuilder(StoreManagerInterface::class) ->disableOriginalConstructor() ->getMock(); - $this->orderRepositoryMock = $this->getMockBuilder(OrderRepositoryInterface::class) + $this->orderItemRepositoryMock = $this->getMockBuilder(OrderRepositoryInterface::class) ->disableOriginalConstructor() ->getMock(); $this->orderItemRepositoryMock = $this->getMockBuilder(OrderItemRepositoryInterface::class) @@ -127,9 +112,7 @@ public function setUp() $this->stockManagementMock, $this->stockIndexerProcessorMock, $this->priceIndexerMock, - $this->creditmemoRepositoryMock, $this->storeManagerMock, - $this->orderRepositoryMock, $this->orderItemRepositoryMock ); } @@ -139,6 +122,7 @@ public function testExecute() $orderItemId = 99; $productId = 50; $returnToStockItems = [$orderItemId]; + $parentItemId = 52; $qty = 1; $storeId = 0; $webSiteId = 10; @@ -147,10 +131,6 @@ public function testExecute() ->method('getItems') ->willReturn([$this->creditmemoItemMock]); - $this->creditmemoItemMock->expects($this->once()) - ->method('getQty') - ->willReturn($qty); - $this->creditmemoItemMock->expects($this->exactly(2)) ->method('getOrderItemId') ->willReturn($orderItemId); @@ -190,6 +170,14 @@ public function testExecute() ->method('reindexList') ->with([$productId]); + $this->orderItemMock->expects($this->once()) + ->method('getParentItemId') + ->willReturn($parentItemId); + + $this->creditmemoItemMock->expects($this->once()) + ->method('getQty') + ->willReturn($qty); + $this->returnProcessor->execute($this->creditmemoMock, $this->orderMock, $returnToStockItems); } } diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Observer/RefundOrderInventoryObserverTest.php b/app/code/Magento/SalesInventory/Test/Unit/Observer/RefundOrderInventoryObserverTest.php similarity index 53% rename from app/code/Magento/CatalogInventory/Test/Unit/Observer/RefundOrderInventoryObserverTest.php rename to app/code/Magento/SalesInventory/Test/Unit/Observer/RefundOrderInventoryObserverTest.php index f230e75cb385c..95b2067a759df 100644 --- a/app/code/Magento/CatalogInventory/Test/Unit/Observer/RefundOrderInventoryObserverTest.php +++ b/app/code/Magento/SalesInventory/Test/Unit/Observer/RefundOrderInventoryObserverTest.php @@ -3,10 +3,16 @@ * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\CatalogInventory\Test\Unit\Observer; +namespace Magento\SalesInventory\Test\Unit\Observer; -use Magento\CatalogInventory\Observer\RefundOrderInventoryObserver; +use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Model\OrderRepository; +use Magento\SalesInventory\Model\Order\ReturnProcessor; +use Magento\SalesInventory\Observer\RefundOrderInventoryObserver; +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class RefundOrderInventoryObserverTest extends \PHPUnit_Framework_TestCase { /** @@ -44,10 +50,30 @@ class RefundOrderInventoryObserverTest extends \PHPUnit_Framework_TestCase */ protected $eventObserver; + /** + * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + */ + protected $objectManagerHelper; + + /** + * @var OrderRepository|\PHPUnit_Framework_MockObject_MockObject + */ + protected $orderRepositoryMock; + + /** + * @var ReturnProcessor|\PHPUnit_Framework_MockObject_MockObject + */ + protected $returnProcessorMock; + + /** + * @var OrderInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $orderMock; + protected function setUp() { $this->stockIndexerProcessor = $this->getMock( - 'Magento\CatalogInventory\Model\Indexer\Stock\Processor', + \Magento\CatalogInventory\Model\Indexer\Stock\Processor::class, ['reindexList'], [], '', @@ -55,7 +81,7 @@ protected function setUp() ); $this->stockManagement = $this->getMock( - 'Magento\CatalogInventory\Model\StockManagement', + \Magento\CatalogInventory\Model\StockManagement::class, [], [], '', @@ -63,7 +89,7 @@ protected function setUp() ); $this->stockConfiguration = $this->getMockForAbstractClass( - 'Magento\CatalogInventory\Api\StockConfigurationInterface', + \Magento\CatalogInventory\Api\StockConfigurationInterface::class, [ 'isAutoReturnEnabled', 'isDisplayProductStockStatus' @@ -72,16 +98,16 @@ protected function setUp() false ); - $this->priceIndexer = $this->getMockBuilder('Magento\Catalog\Model\Indexer\Product\Price\Processor') + $this->priceIndexer = $this->getMockBuilder(\Magento\Catalog\Model\Indexer\Product\Price\Processor::class) ->disableOriginalConstructor() ->getMock(); - $this->event = $this->getMockBuilder('Magento\Framework\Event') + $this->event = $this->getMockBuilder(\Magento\Framework\Event::class) ->disableOriginalConstructor() ->setMethods(['getProduct', 'getCollection', 'getCreditmemo', 'getQuote', 'getWebsite']) ->getMock(); - $this->eventObserver = $this->getMockBuilder('Magento\Framework\Event\Observer') + $this->eventObserver = $this->getMockBuilder(\Magento\Framework\Event\Observer::class) ->disableOriginalConstructor() ->setMethods(['getEvent']) ->getMock(); @@ -90,8 +116,22 @@ protected function setUp() ->method('getEvent') ->will($this->returnValue($this->event)); - $this->observer = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this))->getObject( - 'Magento\CatalogInventory\Observer\RefundOrderInventoryObserver', + $this->orderRepositoryMock = $this->getMockBuilder(OrderRepository::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->returnProcessorMock = $this->getMockBuilder(ReturnProcessor::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->orderMock = $this->getMockBuilder(OrderInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + + $this->observer = $this->objectManagerHelper->getObject( + \Magento\SalesInventory\Observer\RefundOrderInventoryObserver::class, [ 'stockConfiguration' => $this->stockConfiguration, 'stockManagement' => $this->stockManagement, @@ -99,77 +139,67 @@ protected function setUp() 'priceIndexer' => $this->priceIndexer, ] ); + + $this->objectManagerHelper->setBackwardCompatibleProperty( + $this->observer, + 'orderRepository', + $this->orderRepositoryMock + ); + $this->objectManagerHelper->setBackwardCompatibleProperty( + $this->observer, + 'returnProcessor', + $this->returnProcessorMock + ); } public function testRefundOrderInventory() { - $websiteId = 0; $ids = ['1', '14']; $items = []; $isAutoReturnEnabled = true; - $store = $this->getMock( - 'Magento\Store\Model\Store', - ['getWebsiteId'], - [], - '', - false - ); - $store->expects($this->once())->method('getWebsiteId')->will($this->returnValue($websiteId)); + $creditMemo = $this->getMock(\Magento\Sales\Model\Order\Creditmemo::class, [], [], '', false); - $itemsToUpdate = []; foreach ($ids as $id) { $item = $this->getCreditMemoItem($id); $items[] = $item; - $itemsToUpdate[$item->getProductId()] = $item->getQty(); } - $creditMemo = $this->getMock('Magento\Sales\Model\Order\Creditmemo', [], [], '', false); + $creditMemo->expects($this->once()) - ->method('getAllItems') + ->method('getItems') ->will($this->returnValue($items)); - $creditMemo->expects($this->once())->method('getStore')->will($this->returnValue($store)); $this->stockConfiguration->expects($this->any()) ->method('isAutoReturnEnabled') ->will($this->returnValue($isAutoReturnEnabled)); - $this->stockManagement->expects($this->once()) - ->method('revertProductsSale') - ->with($itemsToUpdate, $websiteId); - - $this->stockIndexerProcessor->expects($this->once()) - ->method('reindexList') - ->with($ids); - - $this->priceIndexer->expects($this->once()) - ->method('reindexList') - ->with($ids); - $this->event->expects($this->once()) ->method('getCreditmemo') ->will($this->returnValue($creditMemo)); + $this->orderRepositoryMock->expects($this->once()) + ->method('get') + ->willReturn($this->orderMock); + + $this->returnProcessorMock->expects($this->once()) + ->method('execute') + ->with($creditMemo, $this->orderMock, $ids, $isAutoReturnEnabled); + $this->observer->execute($this->eventObserver); } private function getCreditMemoItem($productId) { - $parentItemId = false; $backToStock = true; - $qty = 1; $item = $this->getMock( - 'Magento\Sales\Model\Order\Creditmemo\Item', - ['getProductId', 'getOrderItem', 'getBackToStock', 'getQty', '__wakeup'], + \Magento\Sales\Model\Order\Creditmemo\Item::class, + ['getOrderItemId', 'getBackToStock', 'getQty', '__wakeup'], [], '', false ); - $orderItem = $this->getMock('Magento\Sales\Model\Order\Item', ['getParentItemId', '__wakeup'], [], '', false); - $orderItem->expects($this->any())->method('getParentItemId')->willReturn($parentItemId); - $item->expects($this->any())->method('getOrderItem')->willReturn($orderItem); - $item->expects($this->any())->method('getProductId')->will($this->returnValue($productId)); $item->expects($this->any())->method('getBackToStock')->willReturn($backToStock); - $item->expects($this->any())->method('getQty')->willReturn($qty); + $item->expects($this->any())->method('getOrderItemId')->willReturn($productId); return $item; } -} +} \ No newline at end of file diff --git a/app/code/Magento/SalesInventory/composer.json b/app/code/Magento/SalesInventory/composer.json index 44e570caf336b..647aa4464166e 100644 --- a/app/code/Magento/SalesInventory/composer.json +++ b/app/code/Magento/SalesInventory/composer.json @@ -1,26 +1,26 @@ -{ - "name": "magento/module-sales-inventory", - "description": "N/A", - "require": { - "php": "~5.5.0|~5.6.0|~7.0.0", - "magento/module-catalog-inventory": "100.0.*", - "magento/module-sales": "100.0.*", - "magento/module-store": "100.0.*", - "magento/module-catalog": "100.0.*", - "magento/framework": "100.0.*" - }, - "type": "magento2-module", - "version": "100.0.0", - "license": [ - "OSL-3.0", - "AFL-3.0" - ], - "autoload": { - "files": [ - "registration.php" - ], - "psr-4": { - "Magento\\SalesInventory\\": "" - } - } -} +{ + "name": "magento/module-sales-inventory", + "description": "N/A", + "require": { + "php": "~5.5.0|~5.6.0|~7.0.0", + "magento/module-catalog-inventory": "100.0.*", + "magento/module-sales": "100.0.*", + "magento/module-store": "100.0.*", + "magento/module-catalog": "100.0.*", + "magento/framework": "100.0.*" + }, + "type": "magento2-module", + "version": "100.0.0", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\SalesInventory\\": "" + } + } +} diff --git a/app/code/Magento/SalesInventory/etc/events.xml b/app/code/Magento/SalesInventory/etc/events.xml new file mode 100644 index 0000000000000..a71ed7f8a28a1 --- /dev/null +++ b/app/code/Magento/SalesInventory/etc/events.xml @@ -0,0 +1,12 @@ + + + + + + + diff --git a/app/code/Magento/Search/composer.json b/app/code/Magento/Search/composer.json index 19625494b672d..f31d7682a79d9 100644 --- a/app/code/Magento/Search/composer.json +++ b/app/code/Magento/Search/composer.json @@ -10,7 +10,7 @@ "magento/module-reports": "100.0.*" }, "type": "magento2-module", - "version": "100.0.5", + "version": "100.0.6", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Shipping/composer.json b/app/code/Magento/Shipping/composer.json index 8e0ea5cefbad8..38aaca100a316 100644 --- a/app/code/Magento/Shipping/composer.json +++ b/app/code/Magento/Shipping/composer.json @@ -24,7 +24,7 @@ "magento/module-ups": "100.0.*" }, "type": "magento2-module", - "version": "100.0.6", + "version": "100.0.7", "license": [ "OSL-3.0", "AFL-3.0" diff --git a/app/code/Magento/Swatches/Block/Product/Renderer/Listing/Configurable.php b/app/code/Magento/Swatches/Block/Product/Renderer/Listing/Configurable.php index e818ed25d065f..1cf97d1a278ea 100644 --- a/app/code/Magento/Swatches/Block/Product/Renderer/Listing/Configurable.php +++ b/app/code/Magento/Swatches/Block/Product/Renderer/Listing/Configurable.php @@ -39,6 +39,7 @@ protected function getHtmlOutput() */ protected function getSwatchAttributesData() { + $result = []; $swatchAttributeData = parent::getSwatchAttributesData(); foreach ($swatchAttributeData as $attributeId => $item) { if (!empty($item['used_in_product_listing'])) { diff --git a/app/code/Magento/Swatches/Test/Unit/Block/Product/Renderer/Listing/ConfigurableTest.php b/app/code/Magento/Swatches/Test/Unit/Block/Product/Renderer/Listing/ConfigurableTest.php new file mode 100644 index 0000000000000..e22ca83293ede --- /dev/null +++ b/app/code/Magento/Swatches/Test/Unit/Block/Product/Renderer/Listing/ConfigurableTest.php @@ -0,0 +1,217 @@ +arrayUtils = $this->getMock('\Magento\Framework\Stdlib\ArrayUtils', [], [], '', false); + $this->jsonEncoder = $this->getMock('\Magento\Framework\Json\EncoderInterface', [], [], '', false); + $this->helper = $this->getMock('\Magento\ConfigurableProduct\Helper\Data', [], [], '', false); + $this->swatchHelper = $this->getMock('\Magento\Swatches\Helper\Data', [], [], '', false); + $this->swatchMediaHelper = $this->getMock('\Magento\Swatches\Helper\Media', [], [], '', false); + $this->catalogProduct = $this->getMock('\Magento\Catalog\Helper\Product', [], [], '', false); + $this->currentCustomer = $this->getMock('\Magento\Customer\Helper\Session\CurrentCustomer', [], [], '', false); + $this->priceCurrency = $this->getMock('\Magento\Framework\Pricing\PriceCurrencyInterface', [], [], '', false); + $this->configurableAttributeData = $this->getMock( + 'Magento\ConfigurableProduct\Model\ConfigurableAttributeData', + [], + [], + '', + false + ); + $this->product = $this->getMock('\Magento\Catalog\Model\Product', [], [], '', false); + $this->typeInstance = $this->getMock('\Magento\Catalog\Model\Product\Type\AbstractType', [], [], '', false); + $this->scopeConfig = $this->getMock('\Magento\Framework\App\Config\ScopeConfigInterface', [], [], '', false); + $this->imageHelper = $this->getMock('\Magento\Catalog\Helper\Image', [], [], '', false); + $this->urlBuilder = $this->getMock('\Magento\Framework\UrlInterface'); + + $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->configurable = $objectManagerHelper->getObject( + '\Magento\Swatches\Block\Product\Renderer\Listing\Configurable', + [ + 'scopeConfig' => $this->scopeConfig, + 'imageHelper' => $this->imageHelper, + 'urlBuilder' => $this->urlBuilder, + 'arrayUtils' => $this->arrayUtils, + 'jsonEncoder' => $this->jsonEncoder, + 'helper' => $this->helper, + 'swatchHelper' => $this->swatchHelper, + 'swatchMediaHelper' => $this->swatchMediaHelper, + 'catalogProduct' => $this->catalogProduct, + 'currentCustomer' => $this->currentCustomer, + 'priceCurrency' => $this->priceCurrency, + 'configurableAttributeData' => $this->configurableAttributeData, + 'data' => [], + ] + ); + } + + /** + * @covers Magento\Swatches\Block\Product\Renderer\Listing\Configurable::getSwatchAttributesData + */ + public function testGetJsonSwatchConfigWithoutSwatches() + { + $this->prepareGetJsonSwatchConfig(); + $this->configurable->setProduct($this->product); + $this->swatchHelper->expects($this->once())->method('getSwatchAttributesAsArray') + ->with($this->product) + ->willReturn([]); + $this->swatchHelper->expects($this->once())->method('getSwatchesByOptionsId') + ->willReturn([]); + $this->jsonEncoder->expects($this->once())->method('encode')->with([]); + $this->configurable->getJsonSwatchConfig(); + } + + /** + * @covers Magento\Swatches\Block\Product\Renderer\Listing\Configurable::getSwatchAttributesData + */ + public function testGetJsonSwatchNotUsedInProductListing() + { + $this->prepareGetJsonSwatchConfig(); + $this->configurable->setProduct($this->product); + $this->swatchHelper->expects($this->once())->method('getSwatchAttributesAsArray') + ->with($this->product) + ->willReturn([ + 1 => [ + 'options' => [1 => 'testA', 3 => 'testB'], + 'use_product_image_for_swatch' => true, + 'used_in_product_listing' => false, + 'attribute_code' => 'code', + ], + ]); + $this->swatchHelper->expects($this->once())->method('getSwatchesByOptionsId') + ->willReturn([]); + $this->jsonEncoder->expects($this->once())->method('encode')->with([]); + $this->configurable->getJsonSwatchConfig(); + } + + /** + * @covers Magento\Swatches\Block\Product\Renderer\Listing\Configurable::getSwatchAttributesData + */ + public function testGetJsonSwatchUsedInProductListing() + { + $products = [ + 1 => 'testA', + 3 => 'testB' + ]; + $expected = + [ + 'type' => null, + 'value' => 'hello', + 'label' => $products[3] + ]; + $this->prepareGetJsonSwatchConfig(); + $this->configurable->setProduct($this->product); + $this->swatchHelper->expects($this->once())->method('getSwatchAttributesAsArray') + ->with($this->product) + ->willReturn([ + 1 => [ + 'options' => $products, + 'use_product_image_for_swatch' => true, + 'used_in_product_listing' => true, + 'attribute_code' => 'code', + ], + ]); + $this->swatchHelper->expects($this->once())->method('getSwatchesByOptionsId') + ->with([1, 3]) + ->willReturn([ + 3 => ['type' => $expected['type'], 'value' => $expected['value']] + ]); + $this->jsonEncoder->expects($this->once())->method('encode'); + $this->configurable->getJsonSwatchConfig(); + } + + private function prepareGetJsonSwatchConfig() + { + $product1 = $this->getMock('\Magento\Catalog\Model\Product', [], [], '', false); + $product1->expects($this->atLeastOnce())->method('isSaleable')->willReturn(true); + $product1->expects($this->any())->method('getData')->with('code')->willReturn(1); + + $product2 = $this->getMock('\Magento\Catalog\Model\Product', [], [], '', false); + $product2->expects($this->atLeastOnce())->method('isSaleable')->willReturn(true); + $product2->expects($this->any())->method('getData')->with('code')->willReturn(3); + + $simpleProducts = [$product1, $product2]; + $configurableType = $this->getMock( + '\Magento\ConfigurableProduct\Model\Product\Type\Configurable', + [], + [], + '', + false + ); + $configurableType->expects($this->atLeastOnce())->method('getUsedProducts')->with($this->product, null) + ->willReturn($simpleProducts); + $this->product->expects($this->any())->method('getTypeInstance')->willReturn($configurableType); + + $productAttribute1 = $this->getMock('\Magento\Eav\Model\Entity\Attribute\AbstractAttribute', [], [], '', false); + $productAttribute1->expects($this->any())->method('getId')->willReturn(1); + $productAttribute1->expects($this->any())->method('getAttributeCode')->willReturn('code'); + + $attribute1 = $this->getMock( + '\Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute', + ['getProductAttribute'], + [], + '', + false + ); + $attribute1->expects($this->any())->method('getProductAttribute')->willReturn($productAttribute1); + + $this->helper->expects($this->any())->method('getAllowAttributes')->with($this->product) + ->willReturn([$attribute1]); + } +} diff --git a/app/code/Magento/Swatches/view/frontend/templates/product/layered/renderer.phtml b/app/code/Magento/Swatches/view/frontend/templates/product/layered/renderer.phtml index 78c1b50da9fa3..9067552b7f23b 100644 --- a/app/code/Magento/Swatches/view/frontend/templates/product/layered/renderer.phtml +++ b/app/code/Magento/Swatches/view/frontend/templates/product/layered/renderer.phtml @@ -69,7 +69,7 @@