diff --git a/app/code/Magento/OfflineShipping/Model/Quote/Address/FreeShipping.php b/app/code/Magento/OfflineShipping/Model/Quote/Address/FreeShipping.php index d2febaccb2fdf3644774f89626e6224bf6e89415..45c03cffd89823002d8fbed52ec8937cc15312ce 100644 --- a/app/code/Magento/OfflineShipping/Model/Quote/Address/FreeShipping.php +++ b/app/code/Magento/OfflineShipping/Model/Quote/Address/FreeShipping.php @@ -5,8 +5,6 @@ */ namespace Magento\OfflineShipping\Model\Quote\Address; -use Magento\Quote\Model\Quote\Address; - class FreeShipping implements \Magento\Quote\Model\Quote\Address\FreeShippingInterface { /** @@ -66,10 +64,14 @@ class FreeShipping implements \Magento\Quote\Model\Quote\Address\FreeShippingInt $itemFreeShipping = (bool)$item->getFreeShipping(); $addressFreeShipping = $addressFreeShipping && $itemFreeShipping; + if ($addressFreeShipping && !$item->getAddress()->getFreeShipping()) { + $item->getAddress()->setFreeShipping(true); + } + /** Parent free shipping we apply to all children*/ $this->applyToChildren($item, $itemFreeShipping); } - return $addressFreeShipping; + return (bool)$quote->getShippingAddress()->getFreeShipping(); } /** diff --git a/app/code/Magento/OfflineShipping/Test/Unit/Model/Quote/Address/FreeShippingTest.php b/app/code/Magento/OfflineShipping/Test/Unit/Model/Quote/Address/FreeShippingTest.php new file mode 100644 index 0000000000000000000000000000000000000000..242a7df4b2298230fad773f0e0e4f6711e189ad4 --- /dev/null +++ b/app/code/Magento/OfflineShipping/Test/Unit/Model/Quote/Address/FreeShippingTest.php @@ -0,0 +1,117 @@ +<?php +/** + * Copyright © 2016 Magento. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\OfflineShipping\Test\Unit\Model\Quote\Address; + +class FreeShippingTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Magento\OfflineShipping\Model\Quote\Address\FreeShipping + */ + private $model; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Store\Model\StoreManagerInterface + */ + private $storeManagerMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\OfflineShipping\Model\SalesRule\Calculator + */ + private $calculatorMock; + + protected function setUp() + { + $this->storeManagerMock = $this->getMock(\Magento\Store\Model\StoreManagerInterface::class); + $this->calculatorMock = $this->getMock( + \Magento\OfflineShipping\Model\SalesRule\Calculator::class, + [], + [], + '', + false + ); + + $this->model = new \Magento\OfflineShipping\Model\Quote\Address\FreeShipping( + $this->storeManagerMock, + $this->calculatorMock + ); + } + + public function testIsFreeShippingIfNoItems() + { + $quoteMock = $this->getMock(\Magento\Quote\Model\Quote::class, [], [], '', false); + $this->assertFalse($this->model->isFreeShipping($quoteMock, [])); + } + + public function testIsFreeShipping() + { + $storeId = 100; + $websiteId = 200; + $customerGroupId = 300; + $quoteMock = $this->getMock( + \Magento\Quote\Model\Quote::class, + ['getShippingAddress', 'getStoreId', 'getCustomerGroupId', 'getCouponCode'], + [], + '', + false + ); + $itemMock = $this->getMock( + \Magento\Quote\Model\Quote\Item::class, + [ + 'getNoDiscount', + 'getParentItemId', + 'getFreeShipping', + 'getAddress', + 'isChildrenCalculated', + 'getHasChildren', + 'getChildren' + ], + [], + '', + false + ); + + $quoteMock->expects($this->once())->method('getStoreId')->willReturn($storeId); + $storeMock = $this->getMock(\Magento\Store\Api\Data\StoreInterface::class); + $storeMock->expects($this->once())->method('getWebsiteId')->willReturn($websiteId); + $this->storeManagerMock->expects($this->once())->method('getStore')->with($storeId)->willReturn($storeMock); + + $quoteMock->expects($this->once())->method('getCustomerGroupId')->willReturn($customerGroupId); + $quoteMock->expects($this->once())->method('getCouponCode')->willReturn(null); + + $this->calculatorMock->expects($this->once()) + ->method('init') + ->with($websiteId, $customerGroupId, null) + ->willReturnSelf(); + + $itemMock->expects($this->once())->method('getNoDiscount')->willReturn(false); + $itemMock->expects($this->once())->method('getParentItemId')->willReturn(false); + $this->calculatorMock->expects($this->exactly(2))->method('processFreeShipping')->willReturnSelf(); + $itemMock->expects($this->once())->method('getFreeShipping')->willReturn(true); + + $addressMock = $this->getMock( + \Magento\Quote\Model\Quote\Address::class, + ['getFreeShipping', 'setFreeShipping'], + [], + '', + false + ); + $itemMock->expects($this->exactly(2))->method('getAddress')->willReturn($addressMock); + $addressMock->expects($this->at(1))->method('getFreeShipping')->willReturn(false); + $addressMock->expects($this->at(2))->method('getFreeShipping')->willReturn(true); + $addressMock->expects($this->once())->method('setFreeShipping')->with(true)->willReturnSelf(); + $quoteMock->expects($this->once())->method('getShippingAddress')->willReturn($addressMock); + + $itemMock->expects($this->once())->method('getHasChildren')->willReturn(true); + $itemMock->expects($this->once())->method('isChildrenCalculated')->willReturn(true); + + $childMock = $this->getMock(\Magento\Quote\Model\Quote\Item::class, ['setFreeShipping'], [], '', false); + $childMock->expects($this->once())->method('setFreeShipping')->with(true)->willReturnSelf(); + $itemMock->expects($this->once())->method('getChildren')->willReturn([$childMock]); + + $this->assertTrue($this->model->isFreeShipping($quoteMock, [$itemMock])); + } +}