diff --git a/app/code/Magento/Checkout/Block/Cart/AbstractCart.php b/app/code/Magento/Checkout/Block/Cart/AbstractCart.php index 95b5cda03e38a..b4f14959357fb 100644 --- a/app/code/Magento/Checkout/Block/Cart/AbstractCart.php +++ b/app/code/Magento/Checkout/Block/Cart/AbstractCart.php @@ -149,7 +149,11 @@ public function getTotals() public function getTotalsCache() { if (empty($this->_totals)) { - $this->_totals = $this->getQuote()->getTotals(); + if ($this->getQuote()->isVirtual()) { + $this->_totals = $this->getQuote()->getBillingAddress()->getTotals(); + } else { + $this->_totals = $this->getQuote()->getShippingAddress()->getTotals(); + } } return $this->_totals; } diff --git a/app/code/Magento/Checkout/Model/DefaultConfigProvider.php b/app/code/Magento/Checkout/Model/DefaultConfigProvider.php index a02455a1068aa..d0cc39ceaafe1 100644 --- a/app/code/Magento/Checkout/Model/DefaultConfigProvider.php +++ b/app/code/Magento/Checkout/Model/DefaultConfigProvider.php @@ -562,7 +562,11 @@ private function getTotalsData() $totalSegmentsData = []; /** @var \Magento\Quote\Model\Cart\TotalSegment $totalSegment */ foreach ($totals->getTotalSegments() as $totalSegment) { - $totalSegmentsData[] = $totalSegment->toArray(); + $totalSegmentArray = $totalSegment->toArray(); + if (is_object($totalSegment->getExtensionAttributes())) { + $totalSegmentArray['extension_attributes'] = $totalSegment->getExtensionAttributes()->__toArray(); + } + $totalSegmentsData[] = $totalSegmentArray; } $totals->setItems($items); $totals->setTotalSegments($totalSegmentsData); diff --git a/app/code/Magento/Checkout/Model/ShippingInformationManagement.php b/app/code/Magento/Checkout/Model/ShippingInformationManagement.php index 338a8e053e344..2858cc787163d 100644 --- a/app/code/Magento/Checkout/Model/ShippingInformationManagement.php +++ b/app/code/Magento/Checkout/Model/ShippingInformationManagement.php @@ -62,6 +62,11 @@ class ShippingInformationManagement implements \Magento\Checkout\Api\ShippingInf */ protected $scopeConfig; + /** + * @var \Magento\Quote\Model\Quote\TotalsCollector + */ + protected $totalsCollector; + /** * @param \Magento\Quote\Api\PaymentMethodManagementInterface $paymentMethodManagement * @param \Magento\Checkout\Model\PaymentDetailsFactory $paymentDetailsFactory @@ -71,6 +76,7 @@ class ShippingInformationManagement implements \Magento\Checkout\Api\ShippingInf * @param Logger $logger * @param \Magento\Customer\Api\AddressRepositoryInterface $addressRepository * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + * @param \Magento\Quote\Model\Quote\TotalsCollector $totalsCollector * @codeCoverageIgnore */ public function __construct( @@ -81,7 +87,8 @@ public function __construct( QuoteAddressValidator $addressValidator, Logger $logger, \Magento\Customer\Api\AddressRepositoryInterface $addressRepository, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, + \Magento\Quote\Model\Quote\TotalsCollector $totalsCollector ) { $this->paymentMethodManagement = $paymentMethodManagement; $this->paymentDetailsFactory = $paymentDetailsFactory; @@ -91,6 +98,7 @@ public function __construct( $this->logger = $logger; $this->addressRepository = $addressRepository; $this->scopeConfig = $scopeConfig; + $this->totalsCollector = $totalsCollector; } /** @@ -132,9 +140,7 @@ public function saveAddressInformation( $address->setShippingMethod($carrierCode . '_' . $methodCode); try { - /** TODO: refactor this code. Eliminate save operation */ - $address->save(); - $address->collectTotals(); + $this->totalsCollector->collectAddressTotals($quote, $address); } catch (\Exception $e) { $this->logger->critical($e); throw new InputException(__('Unable to save address. Please, check input data.')); diff --git a/app/code/Magento/Checkout/Model/Type/Onepage.php b/app/code/Magento/Checkout/Model/Type/Onepage.php index e3c114f9c2209..775c0dcb63232 100644 --- a/app/code/Magento/Checkout/Model/Type/Onepage.php +++ b/app/code/Magento/Checkout/Model/Type/Onepage.php @@ -165,6 +165,11 @@ class Onepage */ protected $dataObjectHelper; + /** + * @var \Magento\Quote\Model\Quote\TotalsCollector + */ + protected $totalsCollector; + /** * @param \Magento\Framework\Event\ManagerInterface $eventManager * @param \Magento\Checkout\Helper\Data $helper @@ -192,6 +197,7 @@ class Onepage * @param \Magento\Framework\Api\ExtensibleDataObjectConverter $extensibleDataObjectConverter * @param \Magento\Quote\Model\QuoteManagement $quoteManagement * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper + * @param \Magento\Quote\Model\Quote\TotalsCollector $totalsCollector * @codeCoverageIgnore * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -221,7 +227,8 @@ public function __construct( \Magento\Quote\Model\QuoteRepository $quoteRepository, \Magento\Framework\Api\ExtensibleDataObjectConverter $extensibleDataObjectConverter, \Magento\Quote\Model\QuoteManagement $quoteManagement, - \Magento\Framework\Api\DataObjectHelper $dataObjectHelper + \Magento\Framework\Api\DataObjectHelper $dataObjectHelper, + \Magento\Quote\Model\Quote\TotalsCollector $totalsCollector ) { $this->_eventManager = $eventManager; $this->_customerUrl = $customerUrl; @@ -249,6 +256,7 @@ public function __construct( $this->extensibleDataObjectConverter = $extensibleDataObjectConverter; $this->quoteManagement = $quoteManagement; $this->dataObjectHelper = $dataObjectHelper; + $this->totalsCollector = $totalsCollector; } /** @@ -490,8 +498,8 @@ public function saveBilling($data, $customerAddressId) ->setSameAsBilling(1) ->setSaveInAddressBook(0) ->setShippingMethod($shippingMethod) - ->setCollectShippingRates(true) - ->collectTotals(); + ->setCollectShippingRates(true); + $this->totalsCollector->collectAddressTotals($this->getQuote(), $shipping); if (!$this->isCheckoutMethodRegister()) { $shipping->save(); @@ -690,7 +698,8 @@ public function saveShipping($data, $customerAddressId) return ['error' => 1, 'message' => $validateRes]; } - $address->collectTotals()->save(); + $this->totalsCollector->collectAddressTotals($this->getQuote(), $address); + $address->save(); $this->getCheckout()->setStepData('shipping', 'complete', true)->setStepData('shipping_method', 'allow', true); diff --git a/app/code/Magento/Checkout/Test/Unit/Block/Cart/AbstractCartTest.php b/app/code/Magento/Checkout/Test/Unit/Block/Cart/AbstractCartTest.php index 56984a205d19c..c900c384d0d8d 100644 --- a/app/code/Magento/Checkout/Test/Unit/Block/Cart/AbstractCartTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Block/Cart/AbstractCartTest.php @@ -109,4 +109,38 @@ public function testGetItemRendererThrowsExceptionForNonexistentRenderer() $block->getItemRenderer('some-type'); } + + /** + * @param array $expectedResult + * @param bool $isVirtual + * @dataProvider getTotalsCacheDataProvider + */ + public function testGetTotalsCache($expectedResult, $isVirtual) + { + $totals = $isVirtual ? ['billing_totals'] : ['shipping_totals']; + $addressMock = $this->getMock('Magento\Quote\Model\Quote\Address', [], [], '', false); + $checkoutSessionMock = $this->getMock('Magento\Checkout\Model\Session', [], [], '', false); + $quoteMock = $this->getMock('Magento\Quote\Model\Quote', [], [], '', false); + $checkoutSessionMock->expects($this->once())->method('getQuote')->willReturn($quoteMock); + + $quoteMock->expects($this->once())->method('isVirtual')->willReturn($isVirtual); + $quoteMock->expects($this->any())->method('getShippingAddress')->willReturn($addressMock); + $quoteMock->expects($this->any())->method('getBillingAddress')->willReturn($addressMock); + $addressMock->expects($this->once())->method('getTotals')->willReturn($totals); + + /** @var \Magento\Checkout\Block\Cart\AbstractCart $model */ + $model = $this->_objectManager->getObject( + 'Magento\Checkout\Block\Cart\AbstractCart', + ['checkoutSession' => $checkoutSessionMock] + ); + $this->assertEquals($expectedResult, $model->getTotalsCache()); + } + + public function getTotalsCacheDataProvider() + { + return [ + [['billing_totals'], true], + [['shipping_totals'], false] + ]; + } } diff --git a/app/code/Magento/Checkout/Test/Unit/Model/ShippingInformationManagementTest.php b/app/code/Magento/Checkout/Test/Unit/Model/ShippingInformationManagementTest.php index 8021921e17336..552acbb01097b 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/ShippingInformationManagementTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/ShippingInformationManagementTest.php @@ -57,6 +57,11 @@ class ShippingInformationManagementTest extends \PHPUnit_Framework_TestCase */ protected $quoteMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $totalsCollectorMock; + /** * @var \Magento\Checkout\Model\ShippingInformationManagement */ @@ -80,7 +85,8 @@ protected function setUp() $this->loggerMock = $this->getMock('\Psr\Log\LoggerInterface'); $this->addressRepositoryMock = $this->getMock('\Magento\Customer\Api\AddressRepositoryInterface'); $this->scopeConfigMock = $this->getMock('\Magento\Framework\App\Config\ScopeConfigInterface'); - + $this->totalsCollectorMock = + $this->getMock('Magento\Quote\Model\Quote\TotalsCollector', [], [], '', false); $this->model = $objectManager->getObject( '\Magento\Checkout\Model\ShippingInformationManagement', [ @@ -91,7 +97,8 @@ protected function setUp() 'addressValidator' => $this->addressValidatorMock, 'logger' => $this->loggerMock, 'addressRepository' => $this->addressRepositoryMock, - 'scopeConfig' => $this->scopeConfigMock + 'scopeConfig' => $this->scopeConfigMock, + 'totalsCollector' => $this->totalsCollectorMock ] ); @@ -109,7 +116,6 @@ protected function setUp() 'getCountryId', 'importCustomerAddressData', 'save', - 'collectTotals', 'getShippingRateByCode', 'getShippingMethod' ], @@ -294,8 +300,10 @@ public function testSaveAddressInformationThrowExceptionWhileAddressSaving() ->with(true) ->willReturnSelf(); $this->shippingAddressMock->expects($this->once())->method('getCountryId')->willReturn(1); - $this->shippingAddressMock->expects($this->once())->method('save')->willThrowException($exception); - + $this->totalsCollectorMock + ->expects($this->once()) + ->method('collectAddressTotals') + ->willThrowException($exception); $this->addressValidatorMock->expects($this->once()) ->method('validate') ->with($this->shippingAddressMock) @@ -386,8 +394,10 @@ public function testSaveAddressInformationIfCarrierCodeIsInvalid() ->with(true) ->willReturnSelf(); $this->shippingAddressMock->expects($this->once())->method('getCountryId')->willReturn(1); - $this->shippingAddressMock->expects($this->once())->method('save')->willReturnSelf(); - $this->shippingAddressMock->expects($this->once())->method('collectTotals')->willReturnSelf(); + $this->totalsCollectorMock + ->expects($this->once()) + ->method('collectAddressTotals') + ->with($this->quoteMock, $this->shippingAddressMock); $this->shippingAddressMock->expects($this->once())->method('getShippingMethod')->willReturn($shippingMethod); $this->shippingAddressMock->expects($this->once()) ->method('getShippingRateByCode') @@ -464,8 +474,10 @@ public function testSaveAddressInformationIfMinimumAmountIsNotValid() ->with(true) ->willReturnSelf(); $this->shippingAddressMock->expects($this->once())->method('getCountryId')->willReturn(1); - $this->shippingAddressMock->expects($this->once())->method('save')->willReturnSelf(); - $this->shippingAddressMock->expects($this->once())->method('collectTotals')->willReturnSelf(); + $this->totalsCollectorMock + ->expects($this->once()) + ->method('collectAddressTotals') + ->with($this->quoteMock, $this->shippingAddressMock); $this->shippingAddressMock->expects($this->once())->method('getShippingMethod')->willReturn($shippingMethod); $this->shippingAddressMock->expects($this->once()) ->method('getShippingRateByCode') @@ -506,7 +518,6 @@ public function testSaveAddressInformationIfCanNotSaveQuote() ->method('save') ->with($this->quoteMock) ->willThrowException($exception); - $addressInformationMock = $this->getMock('\Magento\Checkout\Api\Data\ShippingInformationInterface'); $addressInformationMock->expects($this->once()) ->method('getShippingAddress') @@ -550,8 +561,11 @@ public function testSaveAddressInformationIfCanNotSaveQuote() ->with(true) ->willReturnSelf(); $this->shippingAddressMock->expects($this->once())->method('getCountryId')->willReturn(1); - $this->shippingAddressMock->expects($this->exactly(2))->method('save')->willReturnSelf(); - $this->shippingAddressMock->expects($this->once())->method('collectTotals')->willReturnSelf(); + $this->shippingAddressMock->expects($this->once())->method('save')->willReturnSelf(); + $this->totalsCollectorMock + ->expects($this->once()) + ->method('collectAddressTotals') + ->with($this->quoteMock, $this->shippingAddressMock); $this->shippingAddressMock->expects($this->once())->method('getShippingMethod')->willReturn($shippingMethod); $this->shippingAddressMock->expects($this->once()) ->method('getShippingRateByCode') @@ -626,8 +640,10 @@ public function testSaveAddressInformation() ->with(true) ->willReturnSelf(); $this->shippingAddressMock->expects($this->once())->method('getCountryId')->willReturn(1); - $this->shippingAddressMock->expects($this->exactly(2))->method('save')->willReturnSelf(); - $this->shippingAddressMock->expects($this->once())->method('collectTotals')->willReturnSelf(); + $this->totalsCollectorMock + ->expects($this->once()) + ->method('collectAddressTotals') + ->with($this->quoteMock, $this->shippingAddressMock); $this->shippingAddressMock->expects($this->once())->method('getShippingMethod')->willReturn($shippingMethod); $this->shippingAddressMock->expects($this->once()) ->method('getShippingRateByCode') diff --git a/app/code/Magento/Checkout/Test/Unit/Model/Type/OnepageTest.php b/app/code/Magento/Checkout/Test/Unit/Model/Type/OnepageTest.php index 52d80ff6c3494..0c319e19e1f37 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/Type/OnepageTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/Type/OnepageTest.php @@ -98,6 +98,9 @@ class OnepageTest extends \PHPUnit_Framework_TestCase /** @var \Magento\Framework\Api\ExtensibleDataObjectConverter|\PHPUnit_Framework_MockObject_MockObject */ protected $extensibleDataObjectConverterMock; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $totalsCollectorMock; + /** * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ @@ -195,6 +198,7 @@ protected function setUp() ->method('toFlatArray') ->will($this->returnValue([])); $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->totalsCollectorMock = $this->getMock('Magento\Quote\Model\Quote\TotalsCollector', [], [], '', false); $this->onepage = $this->objectManagerHelper->getObject( 'Magento\Checkout\Model\Type\Onepage', [ @@ -222,7 +226,8 @@ protected function setUp() 'customerRepository' => $this->customerRepositoryMock, 'extensibleDataObjectConverter' => $this->extensibleDataObjectConverterMock, 'quoteRepository' => $this->quoteRepositoryMock, - 'quoteManagement' => $this->quoteManagementMock + 'quoteManagement' => $this->quoteManagementMock, + 'totalsCollector' => $this->totalsCollectorMock ] ); } @@ -492,8 +497,17 @@ public function testSaveBilling( ->method('setCollectShippingRates') ->will($this->returnSelf()); - $shippingAddressMock->expects($useForShipping ? $this->once() : $this->never()) - ->method('collectTotals'); + if ($useForShipping === \Magento\Checkout\Model\Type\Onepage::USE_FOR_SHIPPING) { + $this->totalsCollectorMock + ->expects($this->once()) + ->method('collectAddressTotals') + ->with($quoteMock, $shippingAddressMock); + } else { + $this->totalsCollectorMock + ->expects($this->never()) + ->method('collectAddressTotals') + ->with($quoteMock, $shippingAddressMock); + } $quoteMock->expects($this->any())->method('setPasswordHash')->with($passwordHash); $quoteMock->expects($this->any())->method('getCheckoutMethod')->will($this->returnValue($checkoutMethod)); diff --git a/app/code/Magento/Msrp/Block/Total.php b/app/code/Magento/Msrp/Block/Total.php index d2775a0ccde6c..9e2ba73829e41 100644 --- a/app/code/Magento/Msrp/Block/Total.php +++ b/app/code/Magento/Msrp/Block/Total.php @@ -10,21 +10,31 @@ */ class Total extends \Magento\Framework\View\Element\Template { - /** @var \Magento\Msrp\Model\Config */ + /** + * @var \Magento\Msrp\Model\Config + */ protected $config; + /** + * @var \Magento\Msrp\Model\Quote\Msrp + */ + protected $msrp; + /** * @param \Magento\Framework\View\Element\Template\Context $context * @param \Magento\Msrp\Model\Config $config + * @param \Magento\Msrp\Model\Quote\Msrp $msrp * @param array $data */ public function __construct( \Magento\Framework\View\Element\Template\Context $context, \Magento\Msrp\Model\Config $config, + \Magento\Msrp\Model\Quote\Msrp $msrp, array $data = [] ) { parent::__construct($context, $data); $this->config = $config; + $this->msrp = $msrp; } /** @@ -35,10 +45,10 @@ protected function _toHtml() /** @var \Magento\Checkout\Block\Cart\AbstractCart $originalBlock */ $originalBlock = $this->getLayout()->getBlock($this->getOriginalBlockName()); $quote = $originalBlock->getQuote(); - if (!$quote->hasCanApplyMsrp() && $this->config->isEnabled()) { + if (!$this->msrp->getCanApplyMsrp($quote->getId()) && $this->config->isEnabled()) { $quote->collectTotals(); } - if ($quote->getCanApplyMsrp()) { + if ($this->msrp->getCanApplyMsrp($quote->getId())) { $originalBlock->setTemplate(''); return parent::_toHtml(); } else { diff --git a/app/code/Magento/Msrp/Model/Quote/Address/CanApplyMsrp.php b/app/code/Magento/Msrp/Model/Quote/Address/CanApplyMsrp.php new file mode 100644 index 0000000000000..07ac9c0144f8b --- /dev/null +++ b/app/code/Magento/Msrp/Model/Quote/Address/CanApplyMsrp.php @@ -0,0 +1,41 @@ +msrpHelper = $msrpHelper; + } + + /** + * @param \Magento\Quote\Model\Quote\Address $address + * @return bool + */ + public function isCanApplyMsrp($address) + { + $canApplyMsrp = false; + foreach ($address->getAllItems() as $item) { + if (!$item->getParentItemId() + && $this->msrpHelper->isShowBeforeOrderConfirm($item->getProductId()) + && $this->msrpHelper->isMinimalPriceLessMsrp($item->getProductId()) + ) { + $canApplyMsrp = true; + break; + } + } + return $canApplyMsrp; + } +} diff --git a/app/code/Magento/Msrp/Model/Quote/Address/Total.php b/app/code/Magento/Msrp/Model/Quote/Address/Total.php deleted file mode 100644 index 9af7b08e1174e..0000000000000 --- a/app/code/Magento/Msrp/Model/Quote/Address/Total.php +++ /dev/null @@ -1,57 +0,0 @@ -msrpData = $msrpData; - } - - /** - * Collect information about MSRP price enabled - * - * @param \Magento\Quote\Model\Quote\Address $address - * @return $this - * @api - */ - public function collect(\Magento\Quote\Model\Quote\Address $address) - { - parent::collect($address); - - $items = $this->_getAddressItems($address); - if (!count($items)) { - return $this; - } - - $canApplyMsrp = false; - foreach ($items as $item) { - if (!$item->getParentItemId() - && $this->msrpData->isShowBeforeOrderConfirm($item->getProductId()) - && $this->msrpData->isMinimalPriceLessMsrp($item->getProductId()) - ) { - $canApplyMsrp = true; - break; - } - } - - $address->setCanApplyMsrp($canApplyMsrp); - - return $this; - } -} diff --git a/app/code/Magento/Msrp/Model/Quote/Msrp.php b/app/code/Magento/Msrp/Model/Quote/Msrp.php new file mode 100644 index 0000000000000..3e1cf94baae92 --- /dev/null +++ b/app/code/Magento/Msrp/Model/Quote/Msrp.php @@ -0,0 +1,41 @@ +canApplyMsrpData[$quoteId] = (bool)$canApply; + return $this; + } + + /** + * @param int $quoteId + * @return bool + * @SuppressWarnings(PHPMD.BooleanGetMethodName) + */ + public function getCanApplyMsrp($quoteId) + { + if (isset($this->canApplyMsrpData[$quoteId])) { + return (bool)$this->canApplyMsrpData[$quoteId]; + } + return false; + } +} diff --git a/app/code/Magento/Msrp/Observer/Frontend/Quote/SetCanApplyMsrpObserver.php b/app/code/Magento/Msrp/Observer/Frontend/Quote/SetCanApplyMsrpObserver.php index c91b321d05160..0e1bf4ea27180 100644 --- a/app/code/Magento/Msrp/Observer/Frontend/Quote/SetCanApplyMsrpObserver.php +++ b/app/code/Magento/Msrp/Observer/Frontend/Quote/SetCanApplyMsrpObserver.php @@ -5,7 +5,6 @@ */ namespace Magento\Msrp\Observer\Frontend\Quote; -use Magento\Msrp\Model\Config; use Magento\Framework\Event\ObserverInterface; /** @@ -13,15 +12,34 @@ */ class SetCanApplyMsrpObserver implements ObserverInterface { - /** @var Config */ + /** + * @var \Magento\Msrp\Model\Config + */ protected $config; /** - * @param Config $config + * @var \Magento\Msrp\Model\Quote\Address\CanApplyMsrp */ - public function __construct(Config $config) - { + protected $canApplyMsrp; + + /** + * @var \Magento\Msrp\Model\Quote\Msrp + */ + protected $msrp; + + /** + * @param \Magento\Msrp\Model\Config $config + * @param \Magento\Msrp\Model\Quote\Address\CanApplyMsrp $canApplyMsrp + * @param \Magento\Msrp\Model\Quote\Msrp $msrp + */ + public function __construct( + \Magento\Msrp\Model\Config $config, + \Magento\Msrp\Model\Quote\Address\CanApplyMsrp $canApplyMsrp, + \Magento\Msrp\Model\Quote\Msrp $msrp + ) { $this->config = $config; + $this->canApplyMsrp = $canApplyMsrp; + $this->msrp = $msrp; } /** @@ -38,13 +56,12 @@ public function execute(\Magento\Framework\Event\Observer $observer) $canApplyMsrp = false; if ($this->config->isEnabled()) { foreach ($quote->getAllAddresses() as $address) { - if ($address->getCanApplyMsrp()) { + if ($this->canApplyMsrp->isCanApplyMsrp($address)) { $canApplyMsrp = true; break; } } } - - $quote->setCanApplyMsrp($canApplyMsrp); + $this->msrp->setCanApplyMsrp($quote->getId(), $canApplyMsrp); } } diff --git a/app/code/Magento/Msrp/Test/Unit/Observer/Frontend/Quote/SetCanApplyMsrpObserverTest.php b/app/code/Magento/Msrp/Test/Unit/Observer/Frontend/Quote/SetCanApplyMsrpObserverTest.php index c94a8303b0436..37f016065901a 100644 --- a/app/code/Magento/Msrp/Test/Unit/Observer/Frontend/Quote/SetCanApplyMsrpObserverTest.php +++ b/app/code/Magento/Msrp/Test/Unit/Observer/Frontend/Quote/SetCanApplyMsrpObserverTest.php @@ -6,7 +6,6 @@ namespace Magento\Msrp\Test\Unit\Observer\Frontend\Quote; use Magento\Quote\Model\Quote\Address; -use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; /** * Tests Magento\Msrp\Model\Observer\Frontend\Quote\SetCanApplyMsrp @@ -23,72 +22,77 @@ class SetCanApplyMsrpObserverTest extends \PHPUnit_Framework_TestCase */ protected $configMock; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $canApplyMsrpMock; + + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $msrpMock; + protected function setUp() { - $this->configMock = $this->getMockBuilder('Magento\Msrp\Model\Config') - ->disableOriginalConstructor() - ->getMock(); - $this->observer = (new ObjectManager($this))->getObject( - 'Magento\Msrp\Observer\Frontend\Quote\SetCanApplyMsrpObserver', - ['config' => $this->configMock] + $this->configMock = $this->getMock('\Magento\Msrp\Model\Config', [], [], '', false); + $this->canApplyMsrpMock = $this->getMock('\Magento\Msrp\Model\Quote\Address\CanApplyMsrp', [], [], '', false); + $this->msrpMock = $this->getMock('\Magento\Msrp\Model\Quote\Msrp', [], [], '', false); + + $this->observer = new \Magento\Msrp\Observer\Frontend\Quote\SetCanApplyMsrpObserver( + $this->configMock, + $this->canApplyMsrpMock, + $this->msrpMock ); } - /** - * @param bool $isMsrpEnabled - * @param bool $canApplyMsrp - * @dataProvider setQuoteCanApplyMsrpDataProvider - */ - public function testSetQuoteCanApplyMsrp($isMsrpEnabled, $canApplyMsrp) + public function testSetQuoteCanApplyMsrpIfMsrpCanApply() { - $eventMock = $this->getMockBuilder('Magento\Framework\Event') - ->disableOriginalConstructor() - ->setMethods(['getQuote']) - ->getMock(); - $quoteMock = $this->getMockBuilder('Magento\Quote\Model\Quote') - ->disableOriginalConstructor() - ->setMethods(['__wakeup', 'setCanApplyMsrp', 'getAllAddresses']) - ->getMock(); - $observerMock = $this->getMockBuilder('Magento\Framework\Event\Observer') - ->disableOriginalConstructor() - ->getMock(); - $observerMock->expects($this->once()) - ->method('getEvent') - ->will($this->returnValue($eventMock)); - $eventMock->expects($this->once()) - ->method('getQuote') - ->will($this->returnValue($quoteMock)); - $this->configMock->expects($this->once()) - ->method('isEnabled') - ->will($this->returnValue($isMsrpEnabled)); - $quoteMock->expects($this->once()) - ->method('setCanApplyMsrp') - ->with($canApplyMsrp); - $addressMock1 = $this->getMockBuilder('Magento\Customer\Model\Address\AbstractAddress') - ->disableOriginalConstructor() - ->setMethods(['__wakeup']) - ->getMockForAbstractClass(); - $addressMock1->setCanApplyMsrp($canApplyMsrp); - $addressMock2 = $this->getMockBuilder('Magento\Customer\Model\Address\AbstractAddress') - ->disableOriginalConstructor() - ->setMethods(['__wakeup']) - ->getMockForAbstractClass(); - $addressMock2->setCanApplyMsrp(false); - $quoteMock->expects($this->any()) - ->method('getAllAddresses') - ->will($this->returnValue([$addressMock1, $addressMock2])); + $quoteId = 100; + $eventMock = $this->getMock('\Magento\Framework\Event', ['getQuote'], [], '', false); + $quoteMock = $this->getMock('\Magento\Quote\Model\Quote', ['getAllAddresses', 'getId'], [], '', false); + $observerMock = $this->getMock('\Magento\Framework\Event\Observer', [], [], '', false); + + $observerMock->expects($this->once())->method('getEvent')->willReturn($eventMock); + $eventMock->expects($this->once())->method('getQuote')->willReturn($quoteMock); + $this->configMock->expects($this->once())->method('isEnabled')->willReturn(true); + $this->msrpMock->expects($this->once())->method('setCanApplyMsrp')->with($quoteId, true); + + $addressMock = $this->getMock('\Magento\Customer\Model\Address\AbstractAddress', ['__wakeup'], [], '', false); + $this->canApplyMsrpMock->expects($this->once())->method('isCanApplyMsrp')->willReturn(true); + + $quoteMock->expects($this->once())->method('getAllAddresses')->willReturn([$addressMock]); + $quoteMock->expects($this->once())->method('getId')->willReturn($quoteId); $this->observer->execute($observerMock); } - /** - * @return array - */ public function setQuoteCanApplyMsrpDataProvider() { - return [ - [false, false], - [true, true], - [true, false] - ]; + $quoteId = 100; + $eventMock = $this->getMock('\Magento\Framework\Event', ['getQuote'], [], '', false); + $quoteMock = $this->getMock('\Magento\Quote\Model\Quote', ['getAllAddresses', 'getId'], [], '', false); + $observerMock = $this->getMock('\Magento\Framework\Event\Observer', [], [], '', false); + + $observerMock->expects($this->once())->method('getEvent')->willReturn($eventMock); + $eventMock->expects($this->once())->method('getQuote')->willReturn($quoteMock); + $this->configMock->expects($this->once())->method('isEnabled')->willReturn(true); + $this->msrpMock->expects($this->once())->method('setCanApplyMsrp')->with($quoteId, false); + + $addressMock = $this->getMock('\Magento\Customer\Model\Address\AbstractAddress', ['__wakeup'], [], '', false); + $this->canApplyMsrpMock->expects($this->once())->method('isCanApplyMsrp')->willReturn(false); + + $quoteMock->expects($this->once())->method('getAllAddresses')->willReturn([$addressMock]); + $quoteMock->expects($this->once())->method('getId')->willReturn($quoteId); + $this->observer->execute($observerMock); + } + + public function testSetQuoteCanApplyMsrpIfMsrpDisabled() + { + $quoteId = 100; + $eventMock = $this->getMock('\Magento\Framework\Event', ['getQuote'], [], '', false); + $quoteMock = $this->getMock('\Magento\Quote\Model\Quote', ['getAllAddresses', 'getId'], [], '', false); + $observerMock = $this->getMock('\Magento\Framework\Event\Observer', [], [], '', false); + + $observerMock->expects($this->once())->method('getEvent')->willReturn($eventMock); + $eventMock->expects($this->once())->method('getQuote')->willReturn($quoteMock); + $this->configMock->expects($this->once())->method('isEnabled')->willReturn(false); + $this->msrpMock->expects($this->once())->method('setCanApplyMsrp')->with($quoteId, false); + $quoteMock->expects($this->once())->method('getId')->willReturn($quoteId); + $this->observer->execute($observerMock); } } diff --git a/app/code/Magento/Msrp/composer.json b/app/code/Magento/Msrp/composer.json index b5d603e700e32..efc09c43975ec 100644 --- a/app/code/Magento/Msrp/composer.json +++ b/app/code/Magento/Msrp/composer.json @@ -9,7 +9,6 @@ "magento/module-eav": "1.0.0-beta", "magento/module-grouped-product": "1.0.0-beta", "magento/module-tax": "1.0.0-beta", - "magento/module-quote": "1.0.0-beta", "magento/framework": "1.0.0-beta", "magento/magento-composer-installer": "*" }, diff --git a/app/code/Magento/Msrp/etc/sales.xml b/app/code/Magento/Msrp/etc/sales.xml deleted file mode 100644 index ca96eacc284ff..0000000000000 --- a/app/code/Magento/Msrp/etc/sales.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - -
- - - -
-
diff --git a/app/code/Magento/Multishipping/Block/Checkout/Overview.php b/app/code/Magento/Multishipping/Block/Checkout/Overview.php index 4cccf0ead9090..5155121133e1d 100644 --- a/app/code/Magento/Multishipping/Block/Checkout/Overview.php +++ b/app/code/Magento/Multishipping/Block/Checkout/Overview.php @@ -35,11 +35,23 @@ class Overview extends \Magento\Sales\Block\Items\AbstractItems */ protected $priceCurrency; + /** + * @var \Magento\Quote\Model\Quote\TotalsCollector + */ + protected $totalsCollector; + + /** + * @var \Magento\Quote\Model\Quote\TotalsReader + */ + protected $totalsReader; + /** * @param \Magento\Framework\View\Element\Template\Context $context * @param \Magento\Multishipping\Model\Checkout\Type\Multishipping $multishipping * @param \Magento\Tax\Helper\Data $taxHelper * @param PriceCurrencyInterface $priceCurrency + * @param \Magento\Quote\Model\Quote\TotalsCollector $totalsCollector + * @param \Magento\Quote\Model\Quote\TotalsReader $totalsReader * @param array $data */ public function __construct( @@ -47,6 +59,8 @@ public function __construct( \Magento\Multishipping\Model\Checkout\Type\Multishipping $multishipping, \Magento\Tax\Helper\Data $taxHelper, PriceCurrencyInterface $priceCurrency, + \Magento\Quote\Model\Quote\TotalsCollector $totalsCollector, + \Magento\Quote\Model\Quote\TotalsReader $totalsReader, array $data = [] ) { $this->_taxHelper = $taxHelper; @@ -54,6 +68,8 @@ public function __construct( $this->priceCurrency = $priceCurrency; parent::__construct($context, $data); $this->_isScopePrivate = true; + $this->totalsCollector = $totalsCollector; + $this->totalsReader = $totalsReader; } /** @@ -318,8 +334,8 @@ public function getQuote() */ public function getBillinAddressTotals() { - $_address = $this->getQuote()->getBillingAddress(); - return $this->getShippingAddressTotals($_address); + $address = $this->getQuote()->getBillingAddress(); + return $this->getShippingAddressTotals($address); } /** diff --git a/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php b/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php index 6fb6c15114235..b56d7b532df76 100644 --- a/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php +++ b/app/code/Magento/Multishipping/Model/Checkout/Type/Multishipping.php @@ -134,6 +134,11 @@ class Multishipping extends \Magento\Framework\DataObject */ protected $quoteAddressToOrderAddress; + /** + * @var \Magento\Quote\Model\Quote\TotalsCollector + */ + protected $totalsCollector; + /** * @param \Magento\Checkout\Model\Session $checkoutSession * @param \Magento\Customer\Model\Session $customerSession @@ -155,6 +160,7 @@ class Multishipping extends \Magento\Framework\DataObject * @param \Magento\Quote\Model\QuoteRepository $quoteRepository * @param \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder * @param \Magento\Framework\Api\FilterBuilder $filterBuilder + * @param \Magento\Quote\Model\Quote\TotalsCollector $totalsCollector * @param array $data * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -179,6 +185,7 @@ public function __construct( \Magento\Quote\Model\QuoteRepository $quoteRepository, \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder, \Magento\Framework\Api\FilterBuilder $filterBuilder, + \Magento\Quote\Model\Quote\TotalsCollector $totalsCollector, array $data = [] ) { $this->_eventManager = $eventManager; @@ -201,6 +208,7 @@ public function __construct( $this->quoteItemToOrderItem = $quoteItemToOrderItem; $this->quotePaymentToOrderPayment = $quotePaymentToOrderPayment; $this->quoteAddressToOrderAddress = $quoteAddressToOrderAddress; + $this->totalsCollector = $totalsCollector; parent::__construct($data); $this->_init(); } @@ -523,13 +531,9 @@ public function updateQuoteCustomerShippingAddress($addressId) // } if (isset($address)) { - $this->getQuote()->getShippingAddressByCustomerAddressId( - $addressId - )->setCollectShippingRates( - true - )->importCustomerAddressData( - $address - )->collectTotals(); + $quoteAddress = $this->getQuote()->getShippingAddressByCustomerAddressId($addressId); + $quoteAddress->setCollectShippingRates(true)->importCustomerAddressData($address); + $this->totalsCollector->collectAddressTotals($this->getQuote(), $quoteAddress); $this->quoteRepository->save($this->getQuote()); } @@ -554,7 +558,8 @@ public function setQuoteCustomerBillingAddress($addressId) // } if (isset($address)) { - $this->getQuote()->getBillingAddress($addressId)->importCustomerAddressData($address)->collectTotals(); + $quoteAddress = $this->getQuote()->getBillingAddress($addressId)->importCustomerAddressData($address); + $this->totalsCollector->collectAddressTotals($this->getQuote(), $quoteAddress); $this->getQuote()->collectTotals(); $this->quoteRepository->save($this->getQuote()); } diff --git a/app/code/Magento/Multishipping/Test/Unit/Block/Checkout/OverviewTest.php b/app/code/Magento/Multishipping/Test/Unit/Block/Checkout/OverviewTest.php index 99274bc015f6e..1032c447bcca4 100644 --- a/app/code/Magento/Multishipping/Test/Unit/Block/Checkout/OverviewTest.php +++ b/app/code/Magento/Multishipping/Test/Unit/Block/Checkout/OverviewTest.php @@ -29,6 +29,26 @@ class OverviewTest extends \PHPUnit_Framework_TestCase */ protected $addressMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $totalsReaderMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $totalsCollectorMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $checkoutMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $quoteMock; + protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -49,9 +69,17 @@ protected function setUp() $this->priceCurrencyMock = $this->getMock('Magento\Framework\Pricing\PriceCurrencyInterface', [], [], '', false); + $this->totalsReaderMock = $this->getMock('Magento\Quote\Model\Quote\TotalsReader', [], [], '', false); + $this->totalsCollectorMock = $this->getMock('Magento\Quote\Model\Quote\TotalsCollector', [], [], '', false); + $this->checkoutMock = + $this->getMock('Magento\Multishipping\Model\Checkout\Type\Multishipping', [], [], '', false); + $this->quoteMock = $this->getMock('Magento\Quote\Model\Quote', [], [], '', false); $this->model = $objectManager->getObject('Magento\Multishipping\Block\Checkout\Overview', [ 'priceCurrency' => $this->priceCurrencyMock, + 'totalsCollector' => $this->totalsCollectorMock, + 'totalsReader' => $this->totalsReaderMock, + 'multishipping' => $this->checkoutMock ] ); } @@ -90,23 +118,40 @@ public function testGetShippingAddressItems() public function testGetShippingAddressTotals() { $totalMock = $this->getMock('\Magento\Sales\Model\Order\Total', - [ - 'getCode', - 'setTitle', - '__wakeup' - ], + ['getCode', 'setTitle', '__wakeup'], [], '', - false); - $this->addressMock->expects($this->once())->method('getTotals')->willReturn([$totalMock]); + false + ); $totalMock->expects($this->once())->method('getCode')->willReturn('grand_total'); $this->addressMock->expects($this->once())->method('getAddressType')->willReturn(Address::TYPE_BILLING); + $this->addressMock->expects($this->once())->method('getTotals')->willReturn([$totalMock]); $totalMock->expects($this->once())->method('setTitle')->with('Total'); $this->assertEquals([$totalMock], $this->model->getShippingAddressTotals($this->addressMock)); } public function testGetShippingAddressTotalsWithNotBillingAddress() + { + $totalMock = $this->getMock('\Magento\Sales\Model\Order\Total', + ['getCode', 'setTitle', '__wakeup'], + [], + '', + false + ); + $totalMock->expects($this->once())->method('getCode')->willReturn('grand_total'); + $this->addressMock->expects($this->once())->method('getAddressType')->willReturn('not billing'); + $this->addressMock->expects($this->once())->method('getTotals')->willReturn([$totalMock]); + $totalMock->expects($this->once())->method('setTitle')->with('Total for this address'); + + $this->assertEquals([$totalMock], $this->model->getShippingAddressTotals($this->addressMock)); + } + + /** + * @param \PHPUnit_Framework_MockObject_MockObject $address + * @return \PHPUnit_Framework_MockObject_MockObject + */ + protected function getTotalsMock($address) { $totalMock = $this->getMock('\Magento\Sales\Model\Order\Total', [ @@ -117,11 +162,18 @@ public function testGetShippingAddressTotalsWithNotBillingAddress() [], '', false); - $this->addressMock->expects($this->once())->method('getTotals')->willReturn([$totalMock]); - $totalMock->expects($this->once())->method('getCode')->willReturn('grand_total'); - $this->addressMock->expects($this->once())->method('getAddressType')->willReturn('not billing'); - $totalMock->expects($this->once())->method('setTitle')->with('Total for this address'); - - $this->assertEquals([$totalMock], $this->model->getShippingAddressTotals($this->addressMock)); + $totalsAddressMock = $this->getMock('Magento\Quote\Model\Quote\Address\Total', [], [], '', false); + $this->checkoutMock->expects($this->once())->method('getQuote')->willReturn($this->quoteMock); + $this->totalsCollectorMock + ->expects($this->once()) + ->method('collectAddressTotals') + ->with($this->quoteMock, $address)->willReturn($totalsAddressMock); + $totalsAddressMock->expects($this->once())->method('getData')->willReturn([]); + $this->totalsReaderMock + ->expects($this->once()) + ->method('fetch') + ->with($this->quoteMock, []) + ->willReturn([$totalMock]); + return $totalMock; } } diff --git a/app/code/Magento/Multishipping/Test/Unit/Model/Checkout/Type/MultishippingTest.php b/app/code/Magento/Multishipping/Test/Unit/Model/Checkout/Type/MultishippingTest.php index f66cdc2e22bb5..cb72f0b778a04 100644 --- a/app/code/Magento/Multishipping/Test/Unit/Model/Checkout/Type/MultishippingTest.php +++ b/app/code/Magento/Multishipping/Test/Unit/Model/Checkout/Type/MultishippingTest.php @@ -52,6 +52,11 @@ class MultishippingTest extends \PHPUnit_Framework_TestCase */ protected $searchCriteriaBuilderMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $totalsCollectorMock; + protected function setUp() { $this->checkoutSessionMock = $this->getMock('\Magento\Checkout\Model\Session', [], [], '', false); @@ -97,7 +102,7 @@ protected function setUp() $this->checkoutSessionMock->expects($this->atLeastOnce())->method('getQuote')->willReturn($this->quoteMock); $this->customerSessionMock->expects($this->atLeastOnce())->method('getCustomerDataObject') ->willReturn($this->customerMock); - + $this->totalsCollectorMock = $this->getMock('Magento\Quote\Model\Quote\TotalsCollector', [], [], '', false); $this->model = new \Magento\Multishipping\Model\Checkout\Type\Multishipping( $this->checkoutSessionMock, $this->customerSessionMock, @@ -119,6 +124,7 @@ protected function setUp() $quoteRepositoryMock, $this->searchCriteriaBuilderMock, $this->filterBuilderMock, + $this->totalsCollectorMock, $data ); } diff --git a/app/code/Magento/Multishipping/view/frontend/templates/checkout/overview.phtml b/app/code/Magento/Multishipping/view/frontend/templates/checkout/overview.phtml index a56a4b5b7f636..823361e1d9760 100644 --- a/app/code/Magento/Multishipping/view/frontend/templates/checkout/overview.phtml +++ b/app/code/Magento/Multishipping/view/frontend/templates/checkout/overview.phtml @@ -6,6 +6,7 @@ // @codingStandardsIgnoreFile +/** @var \Magento\Multishipping\Block\Checkout\Overview $block */ ?>
getBlockHtml('formkey'); ?> diff --git a/app/code/Magento/OfflineShipping/Model/Quote/Address/FreeShipping.php b/app/code/Magento/OfflineShipping/Model/Quote/Address/FreeShipping.php new file mode 100644 index 0000000000000..d3c811cf9eca0 --- /dev/null +++ b/app/code/Magento/OfflineShipping/Model/Quote/Address/FreeShipping.php @@ -0,0 +1,91 @@ +storeManager = $storeManager; + $this->calculator = $calculator; + } + + /** + * {@inheritDoc} + */ + public function isFreeShipping(\Magento\Quote\Model\Quote $quote, $items) + { + /** @var \Magento\Quote\Api\Data\CartItemInterface[] $items */ + if (!count($items)) { + return false; + } + + $addressFreeShipping = true; + $store = $this->storeManager->getStore($quote->getStoreId()); + $this->calculator->init( + $store->getWebsiteId(), + $quote->getCustomerGroupId(), + $quote->getCouponCode() + ); + + /** @var \Magento\Quote\Api\Data\CartItemInterface $item */ + foreach ($items as $item) { + if ($item->getNoDiscount()) { + $addressFreeShipping = false; + $item->setFreeShipping(false); + continue; + } + + /** Child item discount we calculate for parent */ + if ($item->getParentItemId()) { + continue; + } + + $this->calculator->processFreeShipping($item); + $itemFreeShipping = (bool)$item->getFreeShipping(); + $addressFreeShipping = $addressFreeShipping && $itemFreeShipping; + + /** Parent free shipping we apply to all children*/ + $this->applyToChildren($item, $itemFreeShipping); + } + return $addressFreeShipping; + } + + /** + * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item + * @param bool $isFreeShipping + * @return void + */ + protected function applyToChildren(\Magento\Quote\Model\Quote\Item\AbstractItem $item, $isFreeShipping) + { + if ($item->getHasChildren() && $item->isChildrenCalculated()) { + foreach ($item->getChildren() as $child) { + $this->calculator->processFreeShipping($child); + if ($isFreeShipping) { + $child->setFreeShipping($isFreeShipping); + } + } + } + } +} diff --git a/app/code/Magento/OfflineShipping/Model/Quote/Freeshipping.php b/app/code/Magento/OfflineShipping/Model/Quote/Freeshipping.php deleted file mode 100644 index efe7c7f63f406..0000000000000 --- a/app/code/Magento/OfflineShipping/Model/Quote/Freeshipping.php +++ /dev/null @@ -1,103 +0,0 @@ -setCode('discount'); - $this->_storeManager = $storeManager; - $this->_calculator = $calculator; - } - - /** - * Collect information about free shipping for all address items - * - * @param \Magento\Quote\Model\Quote\Address $address - * @return \Magento\OfflineShipping\Model\Quote\Freeshipping - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - */ - public function collect(Address $address) - { - parent::collect($address); - $quote = $address->getQuote(); - $store = $this->_storeManager->getStore($quote->getStoreId()); - - $address->setFreeShipping(0); - $items = $this->_getAddressItems($address); - if (!count($items)) { - return $this; - } - $this->_calculator->init($store->getWebsiteId(), $quote->getCustomerGroupId(), $quote->getCouponCode()); - - $isAllFree = true; - foreach ($items as $item) { - if ($item->getNoDiscount()) { - $isAllFree = false; - $item->setFreeShipping(false); - } else { - /** - * Child item discount we calculate for parent - */ - if ($item->getParentItemId()) { - continue; - } - $this->_calculator->processFreeShipping($item); - $isItemFree = (bool)$item->getFreeShipping(); - $isAllFree = $isAllFree && $isItemFree; - if ($item->getHasChildren() && $item->isChildrenCalculated()) { - foreach ($item->getChildren() as $child) { - $this->_calculator->processFreeShipping($child); - /** - * Parent free shipping we apply to all children - */ - if ($isItemFree) { - $child->setFreeShipping($isItemFree); - } - } - } - } - } - if ($isAllFree && !$address->getFreeShipping()) { - $address->setFreeShipping(true); - } - return $this; - } - - /** - * Add information about free shipping for all address items to address object - * By default we not present such information - * - * @param \Magento\Quote\Model\Quote\Address $address - * @return \Magento\OfflineShipping\Model\Quote\Freeshipping - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function fetch(Address $address) - { - return $this; - } -} diff --git a/app/code/Magento/OfflineShipping/etc/di.xml b/app/code/Magento/OfflineShipping/etc/di.xml index 1b547077525ba..7773b54f4e16a 100644 --- a/app/code/Magento/OfflineShipping/etc/di.xml +++ b/app/code/Magento/OfflineShipping/etc/di.xml @@ -9,4 +9,5 @@ + diff --git a/app/code/Magento/OfflineShipping/etc/sales.xml b/app/code/Magento/OfflineShipping/etc/sales.xml deleted file mode 100644 index f7072866e9734..0000000000000 --- a/app/code/Magento/OfflineShipping/etc/sales.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - -
- - - -
-
diff --git a/app/code/Magento/Paypal/Model/Express/Checkout.php b/app/code/Magento/Paypal/Model/Express/Checkout.php index cdf2ba3e7e58c..b1bd45a432ba3 100644 --- a/app/code/Magento/Paypal/Model/Express/Checkout.php +++ b/app/code/Magento/Paypal/Model/Express/Checkout.php @@ -269,6 +269,11 @@ class Checkout */ protected $quoteManagement; + /** + * @var \Magento\Quote\Model\Quote\TotalsCollector + */ + protected $totalsCollector; + /** * @param \Psr\Log\LoggerInterface $logger * @param \Magento\Customer\Model\Url $customerUrl @@ -285,7 +290,7 @@ class Checkout * @param \Magento\Quote\Model\QuoteManagement $quoteManagement * @param \Magento\Paypal\Model\Billing\AgreementFactory $agreementFactory * @param \Magento\Paypal\Model\Api\Type\Factory $apiTypeFactory - * @param \Magento\Framework\DataObject\Copy $objectCopyService + * @param DataObject\Copy $objectCopyService * @param \Magento\Checkout\Model\Session $checkoutSession * @param \Magento\Framework\Encryption\EncryptorInterface $encryptor * @param \Magento\Framework\Message\ManagerInterface $messageManager @@ -294,6 +299,7 @@ class Checkout * @param PaypalQuote $paypalQuote * @param OrderSender $orderSender * @param \Magento\Quote\Model\QuoteRepository $quoteRepository + * @param \Magento\Quote\Model\Quote\TotalsCollector $totalsCollector * @param array $params * @throws \Exception * @SuppressWarnings(PHPMD.ExcessiveParameterList) @@ -323,6 +329,7 @@ public function __construct( PaypalQuote $paypalQuote, OrderSender $orderSender, \Magento\Quote\Model\QuoteRepository $quoteRepository, + \Magento\Quote\Model\Quote\TotalsCollector $totalsCollector, $params = [] ) { $this->quoteManagement = $quoteManagement; @@ -348,6 +355,7 @@ public function __construct( $this->_accountManagement = $accountManagement; $this->paypalQuote = $paypalQuote; $this->quoteRepository = $quoteRepository; + $this->totalsCollector = $totalsCollector; $this->_customerSession = isset($params['session']) && $params['session'] instanceof \Magento\Customer\Model\Session ? $params['session'] : $customerSession; @@ -728,7 +736,8 @@ public function getShippingOptionsCallbackResponse(array $request) foreach ($address->getExportedKeys() as $key) { $quoteAddress->setDataUsingMethod($key, $address->getData($key)); } - $quoteAddress->setCollectShippingRates(true)->collectTotals(); + $quoteAddress->setCollectShippingRates(true); + $this->totalsCollector->collectAddressTotals($this->_quote, $quoteAddress); $options = $this->_prepareShippingOptions($quoteAddress, false, true); } $response = $this->_api->setShippingOptions($options)->formatShippingOptionsCallback(); diff --git a/app/code/Magento/Quote/Api/Data/ShippingAssignmentInterface.php b/app/code/Magento/Quote/Api/Data/ShippingAssignmentInterface.php new file mode 100644 index 0000000000000..3e7dee4a334bc --- /dev/null +++ b/app/code/Magento/Quote/Api/Data/ShippingAssignmentInterface.php @@ -0,0 +1,52 @@ +quoteRepository->getActive($cartId); - $shippingAddress = $quote->getShippingAddress(); if ($quote->isVirtual()) { - $totalsData = array_merge($quote->getBillingAddress()->getData(), $quote->getData()); + $addressTotalsData = $quote->getBillingAddress()->getData(); + $addressTotals = $quote->getBillingAddress()->getTotals(); } else { - $totalsData = array_merge($shippingAddress->getData(), $quote->getData()); + $addressTotalsData = $quote->getShippingAddress()->getData(); + $addressTotals = $quote->getShippingAddress()->getTotals(); } - $totals = $this->totalsFactory->create(); - $this->dataObjectHelper->populateWithArray($totals, $totalsData, '\Magento\Quote\Api\Data\TotalsInterface'); + + /** @var \Magento\Quote\Api\Data\TotalsInterface $quoteTotals */ + $quoteTotals = $this->totalsFactory->create(); + $this->dataObjectHelper->populateWithArray( + $quoteTotals, + $addressTotalsData, + '\Magento\Quote\Api\Data\TotalsInterface' + ); $items = []; - $weeeTaxAppliedAmount = 0; foreach ($quote->getAllVisibleItems() as $index => $item) { $items[$index] = $this->itemConverter->modelToDataObject($item); - $weeeTaxAppliedAmount += $item->getWeeeTaxAppliedRowAmount(); } - $totals->setCouponCode($this->couponService->get($cartId)); - $calculatedTotals = $this->totalsConverter->process($quote->getTotals()); - $amount = $totals->getGrandTotal() - $totals->getTaxAmount(); + $calculatedTotals = $this->totalsConverter->process($addressTotals); + $quoteTotals->setTotalSegments($calculatedTotals); + + $amount = $quoteTotals->getGrandTotal() - $quoteTotals->getTaxAmount(); $amount = $amount > 0 ? $amount : 0; - $totals->setGrandTotal($amount); - $totals->setTotalSegments($calculatedTotals); - $totals->setItems($items); - $totals->setWeeeTaxAppliedAmount($weeeTaxAppliedAmount); - return $totals; + $quoteTotals->setCouponCode($this->couponService->get($cartId)); + $quoteTotals->setGrandTotal($amount); + $quoteTotals->setItems($items); + $quoteTotals->setItemsQty($quote->getItemsQty()); + $quoteTotals->setBaseCurrencyCode($quote->getBaseCurrencyCode()); + $quoteTotals->setQuoteCurrencyCode($quote->getQuoteCurrencyCode()); + return $quoteTotals; } } diff --git a/app/code/Magento/Quote/Model/Cart/TotalsConverter.php b/app/code/Magento/Quote/Model/Cart/TotalsConverter.php index a041844c8ed50..1fa74e907f315 100644 --- a/app/code/Magento/Quote/Model/Cart/TotalsConverter.php +++ b/app/code/Magento/Quote/Model/Cart/TotalsConverter.php @@ -49,7 +49,7 @@ public function process($addressTotals) /** @var \Magento\Quote\Model\Cart\TotalSegment $total */ $total = $this->factory->create(); $total->setData($pureData); - $data[] = $total; + $data[$addressTotal->getCode()] = $total; } return $data; } diff --git a/app/code/Magento/Quote/Model/Quote.php b/app/code/Magento/Quote/Model/Quote.php index f90dbc2ee65f4..a9ba9c9202e91 100644 --- a/app/code/Magento/Quote/Model/Quote.php +++ b/app/code/Magento/Quote/Model/Quote.php @@ -3,9 +3,6 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - namespace Magento\Quote\Model; use Magento\Customer\Api\Data\CustomerInterface; @@ -327,6 +324,26 @@ class Quote extends AbstractExtensibleModel implements \Magento\Quote\Api\Data\C */ protected $extensionAttributesJoinProcessor; + /** + * @var \Magento\Quote\Model\Quote\TotalsCollector + */ + protected $totalsCollector; + + /** + * @var \\Magento\Quote\Model\Quote\TotalsReader + */ + protected $totalsReader; + + /** + * @var \Magento\Quote\Model\ShippingFactory + */ + protected $shippingFactory; + + /** + * @var \Magento\Quote\Model\ShippingAssignmentFactory + */ + protected $shippingAssignmentFactory; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -361,8 +378,12 @@ class Quote extends AbstractExtensibleModel implements \Magento\Quote\Api\Data\C * @param \Magento\Framework\Api\ExtensibleDataObjectConverter $extensibleDataObjectConverter * @param Cart\CurrencyFactory $currencyFactory * @param JoinProcessorInterface $extensionAttributesJoinProcessor - * @param \Magento\Framework\Model\Resource\AbstractResource $resource - * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection + * @param Quote\TotalsCollector $totalsCollector + * @param Quote\TotalsReader $totalsReader + * @param ShippingFactory $shippingFactory + * @param ShippingAssignmentFactory $shippingAssignmentFactory + * @param \Magento\Framework\Model\Resource\AbstractResource|null $resource + * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection * @param array $data * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -400,6 +421,10 @@ public function __construct( \Magento\Framework\Api\ExtensibleDataObjectConverter $extensibleDataObjectConverter, \Magento\Quote\Model\Cart\CurrencyFactory $currencyFactory, JoinProcessorInterface $extensionAttributesJoinProcessor, + Quote\TotalsCollector $totalsCollector, + Quote\TotalsReader $totalsReader, + \Magento\Quote\Model\ShippingFactory $shippingFactory, + \Magento\Quote\Model\ShippingAssignmentFactory $shippingAssignmentFactory, \Magento\Framework\Model\Resource\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, array $data = [] @@ -433,6 +458,10 @@ public function __construct( $this->extensibleDataObjectConverter = $extensibleDataObjectConverter; $this->currencyFactory = $currencyFactory; $this->extensionAttributesJoinProcessor = $extensionAttributesJoinProcessor; + $this->totalsCollector = $totalsCollector; + $this->totalsReader = $totalsReader; + $this->shippingFactory = $shippingFactory; + $this->shippingAssignmentFactory = $shippingAssignmentFactory; parent::__construct( $context, $registry, @@ -735,7 +764,7 @@ public function setStore(\Magento\Store\Model\Store $store) public function getSharedStoreIds() { $ids = $this->_getData('shared_store_ids'); - if (is_null($ids) || !is_array($ids)) { + if ($ids === null || !is_array($ids)) { $website = $this->getWebsite(); if ($website) { return $website->getStoreIds(); @@ -1032,7 +1061,7 @@ public function getCustomerTaxClassId() */ //if (!$this->getData('customer_group_id') && !$this->getData('customer_tax_class_id')) { $groupId = $this->getCustomerGroupId(); - if (!is_null($groupId)) { + if ($groupId !== null) { $taxClassId = $this->groupRepository->getById($this->getCustomerGroupId())->getTaxClassId(); $this->setCustomerTaxClassId($taxClassId); } @@ -1479,7 +1508,7 @@ public function removeItem($itemId) public function removeAllItems() { foreach ($this->getItemsCollection() as $itemId => $item) { - if (is_null($item->getId())) { + if ($item->getId() === null) { $this->getItemsCollection()->removeItemByKey($itemId); } else { $item->isDeleted(true); @@ -1644,8 +1673,8 @@ protected function _addCatalogProduct(\Magento\Catalog\Model\Product $product, $ * It's passed to \Magento\Catalog\Helper\Product->addParamsToBuyRequest() to compose resulting buyRequest. * * Basically it can hold - * - 'current_config', \Magento\Framework\DataObject or array - current buyRequest that configures product in this item, - * used to restore currently attached files + * - 'current_config', \Magento\Framework\DataObject or array - current buyRequest that configures product in this + * item, used to restore currently attached files * - 'files_prefix': string[a-z0-9_] - prefix that was added at frontend to names of file options (file inputs), * so they won't intersect with other submitted options * @@ -1894,141 +1923,25 @@ public function removePayment() */ public function collectTotals() { - /** - * Protect double totals collection - */ if ($this->getTotalsCollectedFlag()) { return $this; } - $this->_eventManager->dispatch( - $this->_eventPrefix . '_collect_totals_before', - [$this->_eventObject => $this] - ); - - $this->_collectItemsQtys(); - - $this->setSubtotal(0); - $this->setBaseSubtotal(0); - - $this->setSubtotalWithDiscount(0); - $this->setBaseSubtotalWithDiscount(0); - - $this->setGrandTotal(0); - $this->setBaseGrandTotal(0); - - foreach ($this->getAllAddresses() as $address) { - $address->setSubtotal(0); - $address->setBaseSubtotal(0); - - $address->setGrandTotal(0); - $address->setBaseGrandTotal(0); - - $address->collectTotals(); - - $this->setSubtotal((float)$this->getSubtotal() + $address->getSubtotal()); - $this->setBaseSubtotal((float)$this->getBaseSubtotal() + $address->getBaseSubtotal()); - - $this->setSubtotalWithDiscount( - (float)$this->getSubtotalWithDiscount() + $address->getSubtotalWithDiscount() - ); - $this->setBaseSubtotalWithDiscount( - (float)$this->getBaseSubtotalWithDiscount() + $address->getBaseSubtotalWithDiscount() - ); - - $this->setGrandTotal((float)$this->getGrandTotal() + $address->getGrandTotal()); - $this->setBaseGrandTotal((float)$this->getBaseGrandTotal() + $address->getBaseGrandTotal()); - } - - $this->quoteValidator->validateQuoteAmount($this, $this->getGrandTotal()); - $this->quoteValidator->validateQuoteAmount($this, $this->getBaseGrandTotal()); - - $this->setData('trigger_recollect', 0); - $this->_validateCouponCode(); - $this->_eventManager->dispatch( - $this->_eventPrefix . '_collect_totals_after', - [$this->_eventObject => $this] - ); + $total = $this->totalsCollector->collect($this); + $this->addData($total->getData()); $this->setTotalsCollectedFlag(true); return $this; } - /** - * Collect items qty - * - * @return $this - */ - protected function _collectItemsQtys() - { - $this->setItemsCount(0); - $this->setItemsQty(0); - $this->setVirtualItemsQty(0); - - foreach ($this->getAllVisibleItems() as $item) { - if ($item->getParentItem()) { - continue; - } - - $children = $item->getChildren(); - if ($children && $item->isShipSeparately()) { - foreach ($children as $child) { - if ($child->getProduct()->getIsVirtual()) { - $this->setVirtualItemsQty($this->getVirtualItemsQty() + $child->getQty() * $item->getQty()); - } - } - } - - if ($item->getProduct()->getIsVirtual()) { - $this->setVirtualItemsQty($this->getVirtualItemsQty() + $item->getQty()); - } - $this->setItemsCount($this->getItemsCount() + 1); - $this->setItemsQty((float)$this->getItemsQty() + $item->getQty()); - } - - return $this; - } - /** * Get all quote totals (sorted by priority) - * Method process quote states isVirtual and isMultiShipping * * @return array */ public function getTotals() { - /** - * If quote is virtual we are using totals of billing address because - * all items assigned to it - */ - if ($this->isVirtual()) { - return $this->getBillingAddress()->getTotals(); - } - - $shippingAddress = $this->getShippingAddress(); - $totals = $shippingAddress->getTotals(); - // Going through all quote addresses and merge their totals - foreach ($this->getAddressesCollection() as $address) { - if ($address->isDeleted() || $address === $shippingAddress) { - continue; - } - foreach ($address->getTotals() as $code => $total) { - if (isset($totals[$code])) { - $totals[$code]->merge($total); - } else { - $totals[$code] = $total; - } - } - } - - $sortedTotals = []; - foreach ($this->getBillingAddress()->getTotalCollector()->getRetrievers() as $total) { - /* @var $total \Magento\Quote\Model\Quote\Address\Total\AbstractTotal */ - if (isset($totals[$total->getCode()])) { - $sortedTotals[$total->getCode()] = $totals[$total->getCode()]; - } - } - return $sortedTotals; + return $this->totalsReader->fetch($this, $this->getData()); } /** @@ -2432,29 +2345,6 @@ public function merge(Quote $quote) return $this; } - /** - * @return $this - */ - protected function _validateCouponCode() - { - $code = $this->_getData('coupon_code'); - if (strlen($code)) { - $addressHasCoupon = false; - $addresses = $this->getAllAddresses(); - if (count($addresses) > 0) { - foreach ($addresses as $address) { - if ($address->hasCouponCode()) { - $addressHasCoupon = true; - } - } - if (!$addressHasCoupon) { - $this->setCouponCode(''); - } - } - } - return $this; - } - /** * Trigger collect totals after loading, if required * diff --git a/app/code/Magento/Quote/Model/Quote/Address.php b/app/code/Magento/Quote/Model/Quote/Address.php index 20f5e96661da5..e228408e19e56 100644 --- a/app/code/Magento/Quote/Model/Quote/Address.php +++ b/app/code/Magento/Quote/Model/Quote/Address.php @@ -223,6 +223,16 @@ class Address extends \Magento\Customer\Model\Address\AbstractAddress implements */ protected $attributeList; + /** + * @var TotalsCollector + */ + protected $totalsCollector; + + /** + * @var \Magento\Quote\Model\Quote\TotalsReader + */ + protected $totalsReader; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -244,16 +254,19 @@ class Address extends \Magento\Customer\Model\Address\AbstractAddress implements * @param Address\RateCollectorInterfaceFactory $rateCollector * @param \Magento\Quote\Model\Resource\Quote\Address\Rate\CollectionFactory $rateCollectionFactory * @param Address\RateRequestFactory $rateRequestFactory - * @param \Magento\Quote\Model\Quote\Address\Total\CollectorFactory $totalCollectorFactory + * @param Address\Total\CollectorFactory $totalCollectorFactory * @param Address\TotalFactory $addressTotalFactory * @param \Magento\Framework\DataObject\Copy $objectCopyService * @param \Magento\Shipping\Model\CarrierFactoryInterface $carrierFactory * @param Address\Validator $validator * @param \Magento\Customer\Model\Address\Mapper $addressMapper - * @param \Magento\Framework\Model\Resource\AbstractResource $resource - * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param Address\CustomAttributeListInterface $attributeList + * @param TotalsCollector $totalsCollector + * @param \Magento\Quote\Model\Quote\TotalsReader $totalsReader + * @param \Magento\Framework\Model\Resource\AbstractResource|null $resource + * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection * @param array $data + * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -284,6 +297,8 @@ public function __construct( Address\Validator $validator, \Magento\Customer\Model\Address\Mapper $addressMapper, Address\CustomAttributeListInterface $attributeList, + \Magento\Quote\Model\Quote\TotalsCollector $totalsCollector, + \Magento\Quote\Model\Quote\TotalsReader $totalsReader, \Magento\Framework\Model\Resource\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, array $data = [] @@ -303,6 +318,8 @@ public function __construct( $this->validator = $validator; $this->addressMapper = $addressMapper; $this->attributeList = $attributeList; + $this->totalsCollector = $totalsCollector; + $this->totalsReader = $totalsReader; parent::__construct( $context, $registry, @@ -964,9 +981,6 @@ public function requestShippingRates(\Magento\Quote\Model\Quote\Item\AbstractIte /** * Store and website identifiers need specify from quote */ - /*$request->setStoreId($this->_storeManager->getStore()->getId()); - $request->setWebsiteId($this->_storeManager->getStore()->getWebsiteId());*/ - $request->setStoreId($this->getQuote()->getStore()->getId()); $request->setWebsiteId($this->getQuote()->getStore()->getWebsiteId()); $request->setFreeShipping($this->getFreeShipping()); @@ -976,8 +990,8 @@ public function requestShippingRates(\Magento\Quote\Model\Quote\Item\AbstractIte $request->setBaseCurrency($this->getQuote()->getStore()->getBaseCurrency()); $request->setPackageCurrency($this->getQuote()->getStore()->getCurrentCurrency()); $request->setLimitCarrier($this->getLimitCarrier()); - - $request->setBaseSubtotalInclTax($this->getBaseSubtotalInclTax()); + $baseSubtotalInclTax = $this->getBaseSubtotalTotalInclTax(); + $request->setBaseSubtotalInclTax($baseSubtotalInclTax); $result = $this->_rateCollector->create()->collectRates($request)->getResult(); @@ -1012,44 +1026,7 @@ public function requestShippingRates(\Magento\Quote\Model\Quote\Item\AbstractIte return $found; } - /** - * Get totals collector model - * - * @return \Magento\Quote\Model\Quote\Address\Total\Collector - */ - public function getTotalCollector() - { - if ($this->_totalCollector === null) { - $this->_totalCollector = $this->_totalCollectorFactory->create( - ['store' => $this->getQuote()->getStore()] - ); - } - - return $this->_totalCollector; - } - - /** - * Collect address totals - * - * @return $this - */ - public function collectTotals() - { - $this->_eventManager->dispatch( - $this->_eventPrefix . '_collect_totals_before', - [$this->_eventObject => $this] - ); - foreach ($this->getTotalCollector()->getCollectors() as $model) { - $model->collect($this); - } - $this->_eventManager->dispatch( - $this->_eventPrefix . '_collect_totals_after', - [$this->_eventObject => $this] - ); - - return $this; - } - + /******************************* Total Collector Interface *******************************************/ /** * Get address totals as array * @@ -1057,8 +1034,10 @@ public function collectTotals() */ public function getTotals() { - foreach ($this->getTotalCollector()->getRetrievers() as $model) { - $model->fetch($this); + $totalsData = array_merge($this->getData(), ['address_quote_items' => $this->getAllItems()]); + $totals = $this->totalsReader->fetch($this->getQuote(), $totalsData); + foreach ($totals as $total) { + $this->addTotal($total); } return $this->_totals; @@ -1067,26 +1046,29 @@ public function getTotals() /** * Add total data or model * - * @param \Magento\Quote\Model\Quote\Total|array $total + * @param \Magento\Quote\Model\Quote\Address\Total|array $total * @return $this */ public function addTotal($total) { + $addressTotal = null; if (is_array($total)) { - $totalInstance = $this->_addressTotalFactory->create( - 'Magento\Quote\Model\Quote\Address\Total' - )->setData( - $total - ); - } elseif ($total instanceof \Magento\Quote\Model\Quote\Total) { - $totalInstance = $total; + /** @var \Magento\Quote\Model\Quote\Address\Total $addressTotal */ + $addressTotal = $this->_addressTotalFactory->create('Magento\Quote\Model\Quote\Address\Total'); + $addressTotal->setData($total); + } elseif ($total instanceof \Magento\Quote\Model\Quote\Address\Total) { + $addressTotal = $total; } - $totalInstance->setAddress($this); - $this->_totals[$totalInstance->getCode()] = $totalInstance; + if ($addressTotal !== null) { + $addressTotal->setAddress($this); + $this->_totals[$addressTotal->getCode()] = $addressTotal; + } return $this; } + /******************************* End Total Collector Interface *******************************************/ + /** * Rewrite clone method * @@ -1174,6 +1156,8 @@ public function setAppliedTaxes($data) return $this->setData('applied_taxes', serialize($data)); } + /******************************* Start Total Collector Interface *******************************************/ + /** * Set shipping amount * @@ -1337,6 +1321,8 @@ public function getAllBaseTotalAmounts() return $this->_baseTotalAmounts; } + /******************************* End Total Collector Interface *******************************************/ + /** * {@inheritdoc} */ diff --git a/app/code/Magento/Quote/Model/Quote/Address/FreeShipping.php b/app/code/Magento/Quote/Model/Quote/Address/FreeShipping.php new file mode 100644 index 0000000000000..46be7a1bf7f98 --- /dev/null +++ b/app/code/Magento/Quote/Model/Quote/Address/FreeShipping.php @@ -0,0 +1,18 @@ +totalAmounts[$code] = $amount; + if ($code != 'subtotal') { + $code = $code . '_amount'; + } + $this->setData($code, $amount); + + return $this; + } + + /** + * Set total amount value in base store currency * - * @param \Magento\Quote\Model\Quote\Address\Total $total + * @param string $code + * @param float $amount * @return $this */ - public function merge(\Magento\Quote\Model\Quote\Address\Total $total) + public function setBaseTotalAmount($code, $amount) { - $newData = $total->getData(); - foreach ($newData as $key => $value) { - if (is_numeric($value)) { - $this->setData($key, $this->_getData($key) + $value); - } + $this->baseTotalAmounts[$code] = $amount; + if ($code != 'subtotal') { + $code = $code . '_amount'; } + $this->setData('base_' . $code, $amount); + + return $this; + } + + /** + * Add amount total amount value + * + * @param string $code + * @param float $amount + * @return $this + */ + public function addTotalAmount($code, $amount) + { + $amount = $this->getTotalAmount($code) + $amount; + $this->setTotalAmount($code, $amount); + return $this; } + + /** + * Add amount total amount value in base store currency + * + * @param string $code + * @param float $amount + * @return $this + */ + public function addBaseTotalAmount($code, $amount) + { + $amount = $this->getBaseTotalAmount($code) + $amount; + $this->setBaseTotalAmount($code, $amount); + + return $this; + } + + /** + * Get total amount value by code + * + * @param string $code + * @return float|int + */ + public function getTotalAmount($code) + { + if (isset($this->totalAmounts[$code])) { + return $this->totalAmounts[$code]; + } + + return 0; + } + + /** + * Get total amount value by code in base store currency + * + * @param string $code + * @return float|int + */ + public function getBaseTotalAmount($code) + { + if (isset($this->baseTotalAmounts[$code])) { + return $this->baseTotalAmounts[$code]; + } + + return 0; + } + + //@codeCoverageIgnoreStart + /** + * Get all total amount values + * + * @return array + */ + public function getAllTotalAmounts() + { + return $this->totalAmounts; + } + + /** + * Get all total amount values in base currency + * + * @return array + */ + public function getAllBaseTotalAmounts() + { + return $this->baseTotalAmounts; + } } diff --git a/app/code/Magento/Quote/Model/Quote/Address/Total/AbstractTotal.php b/app/code/Magento/Quote/Model/Quote/Address/Total/AbstractTotal.php index 2ca58e24ab8fa..cb1021cc487e9 100644 --- a/app/code/Magento/Quote/Model/Quote/Address/Total/AbstractTotal.php +++ b/app/code/Magento/Quote/Model/Quote/Address/Total/AbstractTotal.php @@ -9,7 +9,7 @@ * Sales Quote Address Total abstract model * @SuppressWarnings(PHPMD.NumberOfChildren) */ -abstract class AbstractTotal +abstract class AbstractTotal implements CollectorInterface, ReaderInterface { /** * Total Code name @@ -79,12 +79,18 @@ public function getLabel() /** * Collect totals process. * - * @param \Magento\Quote\Model\Quote\Address $address + * @param \Magento\Quote\Model\Quote $quote + * @param \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment + * @param \Magento\Quote\Model\Quote\Address\Total $total * @return $this */ - public function collect(\Magento\Quote\Model\Quote\Address $address) - { - $this->_setAddress($address); + public function collect( + \Magento\Quote\Model\Quote $quote, + \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment, + \Magento\Quote\Model\Quote\Address\Total $total + ) { + $this->_setAddress($shippingAssignment->getShipping()->getAddress()); + $this->_setTotal($total); /** * Reset amounts */ @@ -96,12 +102,13 @@ public function collect(\Magento\Quote\Model\Quote\Address $address) /** * Fetch (Retrieve data as array) * - * @param \Magento\Quote\Model\Quote\Address $address + * @param \Magento\Quote\Model\Quote $quote + * @param \Magento\Quote\Model\Quote\Address\Total $total * @return array + * @internal param \Magento\Quote\Model\Quote\Address $address */ - public function fetch(\Magento\Quote\Model\Quote\Address $address) + public function fetch(\Magento\Quote\Model\Quote $quote, \Magento\Quote\Model\Quote\Address\Total $total) { - $this->_setAddress($address); return []; } @@ -131,6 +138,29 @@ protected function _getAddress() return $this->_address; } + /** + * @var \Magento\Quote\Model\Quote\Address\Total + */ + protected $total; + + /** + * @param \Magento\Quote\Model\Quote\Address\Total $total + * @return $this + */ + public function _setTotal(\Magento\Quote\Model\Quote\Address\Total $total) + { + $this->total = $total; + return $this; + } + + /** + * @return \Magento\Quote\Model\Quote\Address\Total + */ + protected function _getTotal() + { + return $this->total; + } + /** * Set total model amount value to address * @@ -140,7 +170,7 @@ protected function _getAddress() protected function _setAmount($amount) { if ($this->_canSetAddressAmount) { - $this->_getAddress()->setTotalAmount($this->getCode(), $amount); + $this->_getTotal()->setTotalAmount($this->getCode(), $amount); } return $this; } @@ -155,7 +185,7 @@ protected function _setAmount($amount) protected function _setBaseAmount($baseAmount) { if ($this->_canSetAddressAmount) { - $this->_getAddress()->setBaseTotalAmount($this->getCode(), $baseAmount); + $this->_getTotal()->setBaseTotalAmount($this->getCode(), $baseAmount); } return $this; } @@ -169,7 +199,7 @@ protected function _setBaseAmount($baseAmount) protected function _addAmount($amount) { if ($this->_canAddAmountToAddress) { - $this->_getAddress()->addTotalAmount($this->getCode(), $amount); + $this->_getTotal()->addTotalAmount($this->getCode(), $amount); } return $this; } @@ -183,7 +213,7 @@ protected function _addAmount($amount) protected function _addBaseAmount($baseAmount) { if ($this->_canAddAmountToAddress) { - $this->_getAddress()->addBaseTotalAmount($this->getCode(), $baseAmount); + $this->_getTotal()->addBaseTotalAmount($this->getCode(), $baseAmount); } return $this; } diff --git a/app/code/Magento/Quote/Model/Quote/Address/Total/Collector.php b/app/code/Magento/Quote/Model/Quote/Address/Total/Collector.php index f07c4687ff64b..644d521818b8c 100644 --- a/app/code/Magento/Quote/Model/Quote/Address/Total/Collector.php +++ b/app/code/Magento/Quote/Model/Quote/Address/Total/Collector.php @@ -3,9 +3,6 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - namespace Magento\Quote\Model\Quote\Address\Total; /** @@ -103,7 +100,7 @@ public function getCollectors() /** * Get total models array ordered for right display sequence * - * @return array + * @return \Magento\Quote\Model\Quote\Address\Total\AbstractTotal[] */ public function getRetrievers() { @@ -125,7 +122,8 @@ protected function _initModelInstance($class, $totalCode, $totalConfig) if (!$model instanceof \Magento\Quote\Model\Quote\Address\Total\AbstractTotal) { throw new \Magento\Framework\Exception\LocalizedException( __( - 'The address total model should be extended from \Magento\Quote\Model\Quote\Address\Total\AbstractTotal.' + 'The address total model should be extended from + \Magento\Quote\Model\Quote\Address\Total\AbstractTotal.' ) ); } diff --git a/app/code/Magento/Quote/Model/Quote/Address/Total/CollectorInterface.php b/app/code/Magento/Quote/Model/Quote/Address/Total/CollectorInterface.php new file mode 100644 index 0000000000000..a0efa39830217 --- /dev/null +++ b/app/code/Magento/Quote/Model/Quote/Address/Total/CollectorInterface.php @@ -0,0 +1,21 @@ +setCustbalanceAmount(0); - $address->setBaseCustbalanceAmount(0); - - $address->setGrandTotal($address->getGrandTotal() - $address->getCustbalanceAmount()); - $address->setBaseGrandTotal($address->getBaseGrandTotal() - $address->getBaseCustbalanceAmount()); - - return $this; - } -} diff --git a/app/code/Magento/Quote/Model/Quote/Address/Total/Discount.php b/app/code/Magento/Quote/Model/Quote/Address/Total/Discount.php deleted file mode 100644 index aa50ef3fbe822..0000000000000 --- a/app/code/Magento/Quote/Model/Quote/Address/Total/Discount.php +++ /dev/null @@ -1,154 +0,0 @@ -_eventManager = $eventManager; - $this->_storeManager = $storeManager; - } - - /** - * @param \Magento\Quote\Model\Quote\Address $address - * @return $this - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - */ - public function collect(\Magento\Quote\Model\Quote\Address $address) - { - $quote = $address->getQuote(); - $eventArgs = [ - 'website_id' => $this->_storeManager->getStore($quote->getStoreId())->getWebsiteId(), - 'customer_group_id' => $quote->getCustomerGroupId(), - 'coupon_code' => $quote->getCouponCode(), - ]; - - $address->setFreeShipping(0); - $totalDiscountAmount = 0; - $subtotalWithDiscount = 0; - $baseTotalDiscountAmount = 0; - $baseSubtotalWithDiscount = 0; - - $items = $address->getAllItems(); - if (!count($items)) { - $address->setDiscountAmount($totalDiscountAmount); - $address->setSubtotalWithDiscount($subtotalWithDiscount); - $address->setBaseDiscountAmount($baseTotalDiscountAmount); - $address->setBaseSubtotalWithDiscount($baseSubtotalWithDiscount); - return $this; - } - - foreach ($items as $item) { - if ($item->getNoDiscount()) { - $item->setDiscountAmount(0); - $item->setBaseDiscountAmount(0); - $item->setRowTotalWithDiscount($item->getRowTotal()); - $item->setBaseRowTotalWithDiscount($item->getRowTotal()); - - $subtotalWithDiscount += $item->getRowTotal(); - $baseSubtotalWithDiscount += $item->getBaseRowTotal(); - } else { - /** - * Child item discount we calculate for parent - */ - if ($item->getParentItemId()) { - continue; - } - - /** - * Composite item discount calculation - */ - - if ($item->getHasChildren() && $item->isChildrenCalculated()) { - foreach ($item->getChildren() as $child) { - $eventArgs['item'] = $child; - $this->_eventManager->dispatch('sales_quote_address_discount_item', $eventArgs); - - /** - * Parent free shipping we apply to all children - */ - if ($item->getFreeShipping()) { - $child->setFreeShipping($item->getFreeShipping()); - } - - if (!$child->getDiscountAmount() && $item->getDiscountPercent()) { - $child->setDiscountAmount($child->getRowTotal() * $item->getDiscountPercent()); - } - $totalDiscountAmount += $child->getDiscountAmount(); - //*$item->getQty(); - $baseTotalDiscountAmount += $child->getBaseDiscountAmount(); - //*$item->getQty(); - - $child->setRowTotalWithDiscount($child->getRowTotal() - $child->getDiscountAmount()); - $child->setBaseRowTotalWithDiscount( - $child->getBaseRowTotal() - $child->getBaseDiscountAmount() - ); - - $subtotalWithDiscount += $child->getRowTotalWithDiscount(); - $baseSubtotalWithDiscount += $child->getBaseRowTotalWithDiscount(); - } - } else { - $eventArgs['item'] = $item; - $this->_eventManager->dispatch('sales_quote_address_discount_item', $eventArgs); - - $totalDiscountAmount += $item->getDiscountAmount(); - $baseTotalDiscountAmount += $item->getBaseDiscountAmount(); - - $item->setRowTotalWithDiscount($item->getRowTotal() - $item->getDiscountAmount()); - $item->setBaseRowTotalWithDiscount($item->getBaseRowTotal() - $item->getBaseDiscountAmount()); - - $subtotalWithDiscount += $item->getRowTotalWithDiscount(); - $baseSubtotalWithDiscount += $item->getBaseRowTotalWithDiscount(); - } - } - } - $address->setDiscountAmount($totalDiscountAmount); - $address->setSubtotalWithDiscount($subtotalWithDiscount); - $address->setBaseDiscountAmount($baseTotalDiscountAmount); - $address->setBaseSubtotalWithDiscount($baseSubtotalWithDiscount); - - $address->setGrandTotal($address->getGrandTotal() - $address->getDiscountAmount()); - $address->setBaseGrandTotal($address->getBaseGrandTotal() - $address->getBaseDiscountAmount()); - return $this; - } - - /** - * @param \Magento\Quote\Model\Quote\Address $address - * @return $this - */ - public function fetch(\Magento\Quote\Model\Quote\Address $address) - { - $amount = $address->getDiscountAmount(); - if ($amount != 0) { - $title = __('Discount'); - $code = $address->getCouponCode(); - if (strlen($code)) { - $title = __('Discount (%1)', $code); - } - $address->addTotal(['code' => $this->getCode(), 'title' => $title, 'value' => -$amount]); - } - return $this; - } -} diff --git a/app/code/Magento/Quote/Model/Quote/Address/Total/Grand.php b/app/code/Magento/Quote/Model/Quote/Address/Total/Grand.php index 1ae6838497227..cdc08a78891c2 100644 --- a/app/code/Magento/Quote/Model/Quote/Address/Total/Grand.php +++ b/app/code/Magento/Quote/Model/Quote/Address/Total/Grand.php @@ -10,35 +10,38 @@ class Grand extends \Magento\Quote\Model\Quote\Address\Total\AbstractTotal /** * Collect grand total address amount * - * @param \Magento\Quote\Model\Quote\Address $address - * @return $this + * @param \Magento\Quote\Model\Quote $quote + * @param \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment + * @param \Magento\Quote\Model\Quote\Address\Total $total + * @return $this */ - public function collect(\Magento\Quote\Model\Quote\Address $address) - { - $totals = array_sum($address->getAllTotalAmounts()); - $baseTotals = array_sum($address->getAllBaseTotalAmounts()); + public function collect( + \Magento\Quote\Model\Quote $quote, + \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment, + \Magento\Quote\Model\Quote\Address\Total $total + ) { + $totals = array_sum($total->getAllTotalAmounts()); + $baseTotals = array_sum($total->getAllBaseTotalAmounts()); - $address->setGrandTotal($totals); - $address->setBaseGrandTotal($baseTotals); + $total->setGrandTotal($totals); + $total->setBaseGrandTotal($baseTotals); return $this; } /** * Add grand total information to address * - * @param \Magento\Quote\Model\Quote\Address $address - * @return $this + * @param \Magento\Quote\Model\Quote $quote + * @param \Magento\Quote\Model\Quote\Address\Total $total + * @return $this */ - public function fetch(\Magento\Quote\Model\Quote\Address $address) + public function fetch(\Magento\Quote\Model\Quote $quote, \Magento\Quote\Model\Quote\Address\Total $total) { - $address->addTotal( - [ - 'code' => $this->getCode(), - 'title' => __('Grand Total'), - 'value' => $address->getGrandTotal(), - 'area' => 'footer', - ] - ); - return $this; + return [ + 'code' => $this->getCode(), + 'title' => __('Grand Total'), + 'value' => $total->getGrandTotal(), + 'area' => 'footer', + ]; } } diff --git a/app/code/Magento/Quote/Model/Quote/Address/Total/ReaderInterface.php b/app/code/Magento/Quote/Model/Quote/Address/Total/ReaderInterface.php new file mode 100644 index 0000000000000..6a7850ab24abc --- /dev/null +++ b/app/code/Magento/Quote/Model/Quote/Address/Total/ReaderInterface.php @@ -0,0 +1,16 @@ +priceCurrency = $priceCurrency; + $this->freeShipping = $freeShipping; $this->setCode('shipping'); } /** * Collect totals information about shipping * - * @param \Magento\Quote\Model\Quote\Address $address + * @param \Magento\Quote\Model\Quote $quote + * @param \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment + * @param \Magento\Quote\Model\Quote\Address\Total $total * @return $this * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function collect(\Magento\Quote\Model\Quote\Address $address) - { - parent::collect($address); + public function collect( + \Magento\Quote\Model\Quote $quote, + \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment, + \Magento\Quote\Model\Quote\Address\Total $total + ) { + parent::collect($quote, $shippingAssignment, $total); + + $address = $shippingAssignment->getShipping()->getAddress(); + $method = $shippingAssignment->getShipping()->getMethod(); $address->setWeight(0); $address->setFreeMethodWeight(0); - $this->_setAmount(0)->_setBaseAmount(0); - $items = $this->_getAddressItems($address); - if (!count($items)) { - return $this; - } - - $method = $address->getShippingMethod(); - $freeAddress = $address->getFreeShipping(); $addressWeight = $address->getWeight(); $freeMethodWeight = $address->getFreeMethodWeight(); - $addressQty = 0; + $address->setFreeShipping( + $this->freeShipping->isFreeShipping($quote, $shippingAssignment->getItems()) + ); + $total->setTotalAmount($this->getCode(), 0); + $total->setBaseTotalAmount($this->getCode(), 0); - foreach ($items as $item) { + if (!count($shippingAssignment->getItems())) { + return $this; + } + + $addressQty = 0; + foreach ($shippingAssignment->getItems() as $item) { /** * Skip if this item is virtual */ @@ -80,7 +98,7 @@ public function collect(\Magento\Quote\Model\Quote\Address $address) $itemQty = $child->getTotalQty(); $rowWeight = $itemWeight * $itemQty; $addressWeight += $rowWeight; - if ($freeAddress || $child->getFreeShipping() === true) { + if ($address->getFreeShipping() || $child->getFreeShipping() === true) { $rowWeight = 0; } elseif (is_numeric($child->getFreeShipping())) { $freeQty = $child->getFreeShipping(); @@ -98,7 +116,7 @@ public function collect(\Magento\Quote\Model\Quote\Address $address) $itemWeight = $item->getWeight(); $rowWeight = $itemWeight * $item->getQty(); $addressWeight += $rowWeight; - if ($freeAddress || $item->getFreeShipping() === true) { + if ($address->getFreeShipping() || $item->getFreeShipping() === true) { $rowWeight = 0; } elseif (is_numeric($item->getFreeShipping())) { $freeQty = $item->getFreeShipping(); @@ -118,7 +136,7 @@ public function collect(\Magento\Quote\Model\Quote\Address $address) $itemWeight = $item->getWeight(); $rowWeight = $itemWeight * $item->getQty(); $addressWeight += $rowWeight; - if ($freeAddress || $item->getFreeShipping() === true) { + if ($address->getFreeShipping() || $item->getFreeShipping() === true) { $rowWeight = 0; } elseif (is_numeric($item->getFreeShipping())) { $freeQty = $item->getFreeShipping(); @@ -139,50 +157,47 @@ public function collect(\Magento\Quote\Model\Quote\Address $address) $address->setWeight($addressWeight); $address->setFreeMethodWeight($freeMethodWeight); - $address->collectShippingRates(); - $this->_setAmount(0)->_setBaseAmount(0); - if ($method) { foreach ($address->getAllShippingRates() as $rate) { if ($rate->getCode() == $method) { - $amountPrice = $this->priceCurrency->convert($rate->getPrice(), $address->getQuote()->getStore()); - $this->_setAmount($amountPrice); - $this->_setBaseAmount($rate->getPrice()); + $store = $quote->getStore(); + $amountPrice = $this->priceCurrency->convert( + $rate->getPrice(), + $store + ); + $total->setTotalAmount($this->getCode(), $amountPrice); + $total->setBaseTotalAmount($this->getCode(), $rate->getPrice()); $shippingDescription = $rate->getCarrierTitle() . ' - ' . $rate->getMethodTitle(); $address->setShippingDescription(trim($shippingDescription, ' -')); break; } } } - return $this; } /** * Add shipping totals information to address object * - * @param \Magento\Quote\Model\Quote\Address $address - * @return $this + * @param \Magento\Quote\Model\Quote $quote + * @param \Magento\Quote\Model\Quote\Address\Total $total + * @return array */ - public function fetch(\Magento\Quote\Model\Quote\Address $address) + public function fetch(\Magento\Quote\Model\Quote $quote, \Magento\Quote\Model\Quote\Address\Total $total) { - $amount = $address->getShippingAmount(); - $shippingDescription = $address->getShippingDescription(); - - if ($amount != 0 || $shippingDescription) { - $title = $shippingDescription ? __( - 'Shipping & Handling (%1)', - $shippingDescription - ) : __( - 'Shipping & Handling' - ); - - $address->addTotal(['code' => $this->getCode(), 'title' => $title, 'value' => $amount]); - } - - return $this; + $amount = $total->getShippingAmount(); + $shippingDescription = $total->getShippingDescription(); + $title = ($amount != 0 && $shippingDescription) + ? __('Shipping & Handling (%1)', $shippingDescription) + : __('Shipping & Handling'); + + return [ + 'code' => $this->getCode(), + 'title' => $title, + 'value' => $amount + ]; } /** diff --git a/app/code/Magento/Quote/Model/Quote/Address/Total/Subtotal.php b/app/code/Magento/Quote/Model/Quote/Address/Total/Subtotal.php index 447ab43fa78b8..1889f3586581f 100644 --- a/app/code/Magento/Quote/Model/Quote/Address/Total/Subtotal.php +++ b/app/code/Magento/Quote/Model/Quote/Address/Total/Subtotal.php @@ -29,20 +29,26 @@ public function __construct(\Magento\Quote\Model\QuoteValidator $quoteValidator) /** * Collect address subtotal * - * @param Address $address + * @param \Magento\Quote\Model\Quote $quote + * @param \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment + * @param Address\Total $total * @return $this */ - public function collect(Address $address) - { - parent::collect($address); - $address->setTotalQty(0); + public function collect( + \Magento\Quote\Model\Quote $quote, + \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment, + \Magento\Quote\Model\Quote\Address\Total $total + ) { + parent::collect($quote, $shippingAssignment, $total); + $total->setTotalQty(0); $baseVirtualAmount = $virtualAmount = 0; + $address = $shippingAssignment->getShipping()->getAddress(); /** * Process address items */ - $items = $this->_getAddressItems($address); + $items = $shippingAssignment->getItems(); foreach ($items as $item) { if ($this->_initItem($address, $item) && $item->getQty() > 0) { /** @@ -57,14 +63,14 @@ public function collect(Address $address) } } - $address->setBaseVirtualAmount($baseVirtualAmount); - $address->setVirtualAmount($virtualAmount); + $total->setBaseVirtualAmount($baseVirtualAmount); + $total->setVirtualAmount($virtualAmount); /** * Initialize grand totals */ - $this->quoteValidator->validateQuoteAmount($address->getQuote(), $address->getSubtotal()); - $this->quoteValidator->validateQuoteAmount($address->getQuote(), $address->getBaseSubtotal()); + $this->quoteValidator->validateQuoteAmount($quote, $total->getSubtotal()); + $this->quoteValidator->validateQuoteAmount($quote, $total->getBaseSubtotal()); return $this; } @@ -114,7 +120,6 @@ protected function _initItem($address, $item) $this->_addBaseAmount($item->getBaseRowTotal()); $address->setTotalQty($address->getTotalQty() + $item->getQty()); } - return true; } @@ -156,22 +161,23 @@ protected function _removeItem($address, $item) $address->getQuote()->removeItem($item->getQuoteItemId()); } } - return $this; } /** * Assign subtotal amount and label to address object * - * @param Address $address - * @return $this + * @param \Magento\Quote\Model\Quote $quote + * @param Address\Total $total + * @return array */ - public function fetch(Address $address) + public function fetch(\Magento\Quote\Model\Quote $quote, \Magento\Quote\Model\Quote\Address\Total $total) { - $address->addTotal( - ['code' => $this->getCode(), 'title' => __('Subtotal'), 'value' => $address->getSubtotal()] - ); - return $this; + return [ + 'code' => $this->getCode(), + 'title' => $this->getLabel(), + 'value' => $total->getSubtotal() + ]; } /** diff --git a/app/code/Magento/Quote/Model/Quote/Address/Total/Tax.php b/app/code/Magento/Quote/Model/Quote/Address/Total/Tax.php deleted file mode 100644 index 66854a86ae3f7..0000000000000 --- a/app/code/Magento/Quote/Model/Quote/Address/Total/Tax.php +++ /dev/null @@ -1,297 +0,0 @@ -_taxData = $taxData; - $this->_scopeConfig = $scopeConfig; - $this->_calculation = $calculation; - $this->priceCurrency = $priceCurrency; - $this->setCode('tax'); - } - - /** - * @param \Magento\Quote\Model\Quote\Address $address - * @return $this - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function collect(\Magento\Quote\Model\Quote\Address $address) - { - $store = $address->getQuote()->getStore(); - - $address->setTaxAmount(0); - $address->setBaseTaxAmount(0); - $address->setAppliedTaxes([]); - - $items = $address->getAllItems(); - if (!count($items)) { - return $this; - } - $custTaxClassId = $address->getQuote()->getCustomerTaxClassId(); - - $taxCalculationModel = $this->_calculation; - /* @var $taxCalculationModel \Magento\Tax\Model\Calculation */ - $request = $taxCalculationModel->getRateRequest( - $address, - $address->getQuote()->getBillingAddress(), - $custTaxClassId, - $store - ); - - foreach ($items as $item) { - /** - * Child item's tax we calculate for parent - */ - if ($item->getParentItemId()) { - continue; - } - /** - * We calculate parent tax amount as sum of children's tax amounts - */ - - if ($item->getHasChildren() && $item->isChildrenCalculated()) { - foreach ($item->getChildren() as $child) { - $discountBefore = $item->getDiscountAmount(); - $baseDiscountBefore = $item->getBaseDiscountAmount(); - - $rate = $taxCalculationModel->getRate( - $request->setProductClassId($child->getProduct()->getTaxClassId()) - ); - - $child->setTaxPercent($rate); - $child->calcTaxAmount(); - - if ($discountBefore != $item->getDiscountAmount()) { - $address->setDiscountAmount( - $address->getDiscountAmount() + ($item->getDiscountAmount() - $discountBefore) - ); - $address->setBaseDiscountAmount( - $address->getBaseDiscountAmount() + ($item->getBaseDiscountAmount() - $baseDiscountBefore) - ); - - $address->setGrandTotal( - $address->getGrandTotal() - ($item->getDiscountAmount() - $discountBefore) - ); - $address->setBaseGrandTotal( - $address->getBaseGrandTotal() - ($item->getBaseDiscountAmount() - $baseDiscountBefore) - ); - } - - $this->_saveAppliedTaxes( - $address, - $taxCalculationModel->getAppliedRates($request), - $child->getTaxAmount(), - $child->getBaseTaxAmount(), - $rate - ); - } - $itemTaxAmount = $item->getTaxAmount() + $item->getDiscountTaxCompensation(); - $address->setTaxAmount($address->getTaxAmount() + $itemTaxAmount); - $itemBaseTaxAmount = $item->getBaseTaxAmount() + $item->getBaseDiscountTaxCompensation(); - $address->setBaseTaxAmount($address->getBaseTaxAmount() + $itemBaseTaxAmount); - } else { - $discountBefore = $item->getDiscountAmount(); - $baseDiscountBefore = $item->getBaseDiscountAmount(); - - $rate = $taxCalculationModel->getRate( - $request->setProductClassId($item->getProduct()->getTaxClassId()) - ); - - $item->setTaxPercent($rate); - $item->calcTaxAmount(); - - if ($discountBefore != $item->getDiscountAmount()) { - $address->setDiscountAmount( - $address->getDiscountAmount() + ($item->getDiscountAmount() - $discountBefore) - ); - $address->setBaseDiscountAmount( - $address->getBaseDiscountAmount() + ($item->getBaseDiscountAmount() - $baseDiscountBefore) - ); - - $address->setGrandTotal( - $address->getGrandTotal() - ($item->getDiscountAmount() - $discountBefore) - ); - $address->setBaseGrandTotal( - $address->getBaseGrandTotal() - ($item->getBaseDiscountAmount() - $baseDiscountBefore) - ); - } - - $itemTaxAmount = $item->getTaxAmount() + $item->getDiscountTaxCompensation(); - $address->setTaxAmount($address->getTaxAmount() + $itemTaxAmount); - $itemBaseTaxAmount = $item->getBaseTaxAmount() + $item->getBaseDiscountTaxCompensation(); - $address->setBaseTaxAmount($address->getBaseTaxAmount() + $itemBaseTaxAmount); - - $applied = $taxCalculationModel->getAppliedRates($request); - $this->_saveAppliedTaxes($address, $applied, $item->getTaxAmount(), $item->getBaseTaxAmount(), $rate); - } - } - - $shippingTaxClass = $this->_scopeConfig->getValue( - \Magento\Tax\Model\Config::CONFIG_XML_PATH_SHIPPING_TAX_CLASS, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, - $store - ); - - $shippingTax = 0; - $shippingBaseTax = 0; - - if ($shippingTaxClass) { - $rate = $taxCalculationModel->getRate($request->setProductClassId($shippingTaxClass)); - if ($rate) { - if (!$this->_taxData->shippingPriceIncludesTax()) { - $shippingTax = $address->getShippingAmount() * $rate / 100; - $shippingBaseTax = $address->getBaseShippingAmount() * $rate / 100; - } else { - $shippingTax = $address->getShippingTaxAmount(); - $shippingBaseTax = $address->getBaseShippingTaxAmount(); - } - - $shippingTax = $this->priceCurrency->round($shippingTax); - $shippingBaseTax = $this->priceCurrency->round($shippingBaseTax); - - $address->setTaxAmount($address->getTaxAmount() + $shippingTax); - $address->setBaseTaxAmount($address->getBaseTaxAmount() + $shippingBaseTax); - - $this->_saveAppliedTaxes( - $address, - $taxCalculationModel->getAppliedRates($request), - $shippingTax, - $shippingBaseTax, - $rate - ); - } - } - - if (!$this->_taxData->shippingPriceIncludesTax()) { - $address->setShippingTaxAmount($shippingTax); - $address->setBaseShippingTaxAmount($shippingBaseTax); - } - - $address->setGrandTotal($address->getGrandTotal() + $address->getTaxAmount()); - $address->setBaseGrandTotal($address->getBaseGrandTotal() + $address->getBaseTaxAmount()); - return $this; - } - - /** - * @param \Magento\Quote\Model\Quote\Address $address - * @param array $applied - * @param int $amount - * @param int $baseAmount - * @param int $rate - * @return void - */ - protected function _saveAppliedTaxes( - \Magento\Quote\Model\Quote\Address $address, - $applied, - $amount, - $baseAmount, - $rate - ) { - $previouslyAppliedTaxes = $address->getAppliedTaxes(); - $process = count($previouslyAppliedTaxes); - - foreach ($applied as $row) { - if (!isset($previouslyAppliedTaxes[$row['id']])) { - $row['process'] = $process; - $row['amount'] = 0; - $row['base_amount'] = 0; - $previouslyAppliedTaxes[$row['id']] = $row; - } - - if ($row['percent'] !== null) { - $row['percent'] = $row['percent'] ? $row['percent'] : 1; - $rate = $rate ? $rate : 1; - - $appliedAmount = $amount / $rate * $row['percent']; - $baseAppliedAmount = $baseAmount / $rate * $row['percent']; - } else { - $appliedAmount = 0; - $baseAppliedAmount = 0; - foreach ($row['rates'] as $rate) { - $appliedAmount += $rate['amount']; - $baseAppliedAmount += $rate['base_amount']; - } - } - - if ($appliedAmount || $previouslyAppliedTaxes[$row['id']]['amount']) { - $previouslyAppliedTaxes[$row['id']]['amount'] += $appliedAmount; - $previouslyAppliedTaxes[$row['id']]['base_amount'] += $baseAppliedAmount; - } else { - unset($previouslyAppliedTaxes[$row['id']]); - } - } - $address->setAppliedTaxes($previouslyAppliedTaxes); - } - - /** - * @param \Magento\Quote\Model\Quote\Address $address - * @return $this - */ - public function fetch(\Magento\Quote\Model\Quote\Address $address) - { - $applied = $address->getAppliedTaxes(); - $store = $address->getQuote()->getStore(); - $amount = $address->getTaxAmount(); - - if ($amount != 0 || $this->_taxData->displayZeroTax($store)) { - $address->addTotal( - [ - 'code' => $this->getCode(), - 'title' => __('Tax'), - 'full_info' => $applied ? $applied : [], - 'value' => $amount, - ] - ); - } - return $this; - } -} diff --git a/app/code/Magento/Quote/Model/Quote/Item.php b/app/code/Magento/Quote/Model/Quote/Item.php index 5ead10f925544..39fb2dcbb5b47 100644 --- a/app/code/Magento/Quote/Model/Quote/Item.php +++ b/app/code/Magento/Quote/Model/Quote/Item.php @@ -3,9 +3,6 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - namespace Magento\Quote\Model\Quote; use Magento\Framework\Api\AttributeValueFactory; @@ -367,7 +364,7 @@ public function setQty($qty) public function getQtyOptions() { $qtyOptions = $this->getData('qty_options'); - if (is_null($qtyOptions)) { + if ($qtyOptions === null) { $productIds = []; $qtyOptions = []; foreach ($this->getOptions() as $option) { diff --git a/app/code/Magento/Quote/Model/Quote/TotalsCollector.php b/app/code/Magento/Quote/Model/Quote/TotalsCollector.php new file mode 100644 index 0000000000000..0d7f557e7e7b0 --- /dev/null +++ b/app/code/Magento/Quote/Model/Quote/TotalsCollector.php @@ -0,0 +1,277 @@ +totalCollector = $totalCollector; + $this->totalCollectorFactory = $totalCollectorFactory; + $this->eventManager = $eventManager; + $this->storeManager = $storeManager; + $this->totalFactory = $totalFactory; + $this->collectorList = $collectorList; + $this->shippingFactory = $shippingFactory; + $this->shippingAssignmentFactory = $shippingAssignmentFactory; + $this->quoteValidator = $quoteValidator; + } + + /** + * @param \Magento\Quote\Model\Quote $quote + * @return Address\Total + */ + public function collectQuoteTotals(\Magento\Quote\Model\Quote $quote) + { + if ($quote->isVirtual()) { + return $this->collectAddressTotals($quote, $quote->getBillingAddress()); + } + return $this->collectAddressTotals($quote, $quote->getShippingAddress()); + } + + /** + * @param \Magento\Quote\Model\Quote $quote + * @return \Magento\Quote\Model\Quote\Address\Total + */ + public function collect(\Magento\Quote\Model\Quote $quote) + { + /** @var \Magento\Quote\Model\Quote\Address\Total $total */ + $total = $this->totalFactory->create('Magento\Quote\Model\Quote\Address\Total'); + + $this->eventManager->dispatch( + 'sales_quote_collect_totals_before', + ['quote' => $quote] + ); + + $this->_collectItemsQtys($quote); + + $total->setSubtotal(0); + $total->setBaseSubtotal(0); + + $total->setSubtotalWithDiscount(0); + $total->setBaseSubtotalWithDiscount(0); + + $total->setGrandTotal(0); + $total->setBaseGrandTotal(0); + + /** @var \Magento\Quote\Model\Quote\Address $address */ + foreach ($quote->getAllAddresses() as $address) { + $addressTotal = $this->collectAddressTotals($quote, $address); + + $total->setSubtotal((float)$total->getSubtotal() + $addressTotal->getSubtotal()); + $total->setBaseSubtotal((float)$total->getBaseSubtotal() + $addressTotal->getBaseSubtotal()); + + $total->setSubtotalWithDiscount( + (float)$total->getSubtotalWithDiscount() + $addressTotal->getSubtotalWithDiscount() + ); + $total->setBaseSubtotalWithDiscount( + (float)$total->getBaseSubtotalWithDiscount() + $addressTotal->getBaseSubtotalWithDiscount() + ); + + $total->setGrandTotal((float)$total->getGrandTotal() + $addressTotal->getGrandTotal()); + $total->setBaseGrandTotal((float)$total->getBaseGrandTotal() + $addressTotal->getBaseGrandTotal()); + } + + $this->quoteValidator->validateQuoteAmount($quote, $quote->getGrandTotal()); + $this->quoteValidator->validateQuoteAmount($quote, $quote->getBaseGrandTotal()); + $this->_validateCouponCode($quote); + $this->eventManager->dispatch( + 'sales_quote_collect_totals_after', + ['quote' => $quote] + ); + return $total; + } + + /** + * @param \Magento\Quote\Model\Quote $quote + * @return $this + */ + protected function _validateCouponCode(\Magento\Quote\Model\Quote $quote) + { + $code = $quote->getData('coupon_code'); + if (strlen($code)) { + $addressHasCoupon = false; + $addresses = $quote->getAllAddresses(); + if (count($addresses) > 0) { + foreach ($addresses as $address) { + if ($address->hasCouponCode()) { + $addressHasCoupon = true; + } + } + if (!$addressHasCoupon) { + $quote->setCouponCode(''); + } + } + } + return $this; + } + + /** + * Collect items qty + * + * @param \Magento\Quote\Model\Quote $quote + * @return $this + */ + protected function _collectItemsQtys(\Magento\Quote\Model\Quote $quote) + { + $quote->setItemsCount(0); + $quote->setItemsQty(0); + $quote->setVirtualItemsQty(0); + + foreach ($quote->getAllVisibleItems() as $item) { + if ($item->getParentItem()) { + continue; + } + + $children = $item->getChildren(); + if ($children && $item->isShipSeparately()) { + foreach ($children as $child) { + if ($child->getProduct()->getIsVirtual()) { + $quote->setVirtualItemsQty($quote->getVirtualItemsQty() + $child->getQty() * $item->getQty()); + } + } + } + + if ($item->getProduct()->getIsVirtual()) { + $quote->setVirtualItemsQty($quote->getVirtualItemsQty() + $item->getQty()); + } + $quote->setItemsCount($quote->getItemsCount() + 1); + $quote->setItemsQty((float)$quote->getItemsQty() + $item->getQty()); + } + return $this; + } + + /** + * @param \Magento\Quote\Model\Quote $quote + * @param Address $address + * @return Address\Total + */ + public function collectAddressTotals( + \Magento\Quote\Model\Quote $quote, + \Magento\Quote\Model\Quote\Address $address + ) { + /** @var \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment */ + $shippingAssignment = $this->shippingAssignmentFactory->create(); + + /** @var \Magento\Quote\Api\Data\ShippingInterface $shipping */ + $shipping = $this->shippingFactory->create(); + $shipping->setMethod($address->getShippingMethod()); + $shipping->setAddress($address); + $shippingAssignment->setShipping($shipping); + $shippingAssignment->setItems($address->getAllItems()); + + /** @var \Magento\Quote\Model\Quote\Address\Total $total */ + $total = $this->totalFactory->create('Magento\Quote\Model\Quote\Address\Total'); + $this->eventManager->dispatch( + 'sales_quote_address_collect_totals_before', + [ + 'quote' => $quote, + 'shipping_assignment' => $shippingAssignment, + 'total' => $total + ] + ); + + foreach ($this->collectorList->getCollectors($quote->getStoreId()) as $collector) { + /** @var CollectorInterface $collector */ + $collector->collect($quote, $shippingAssignment, $total); + } + + $this->eventManager->dispatch( + 'sales_quote_address_collect_totals_after', + [ + 'quote' => $quote, + 'shipping_assignment' => $shippingAssignment, + 'total' => $total + ] + ); + + $address->addData($total->getData()); + $address->setAppliedTaxes($total->getAppliedTaxes()); + return $total; + } +} diff --git a/app/code/Magento/Quote/Model/Quote/TotalsCollectorList.php b/app/code/Magento/Quote/Model/Quote/TotalsCollectorList.php new file mode 100644 index 0000000000000..744700dc9926d --- /dev/null +++ b/app/code/Magento/Quote/Model/Quote/TotalsCollectorList.php @@ -0,0 +1,105 @@ +totalCollector = $totalCollector; + $this->totalCollectorFactory = $totalCollectorFactory; + $this->eventManager = $eventManager; + $this->storeManager = $storeManager; + $this->totalFactory = $totalFactory; + } + + /** + * @param int $storeId + * @return Collector + */ + private function getTotalCollector($storeId) + { + if ($this->totalCollector === null) { + $store = $this->storeManager->getStore($storeId); + + $this->totalCollector = $this->totalCollectorFactory->create( + ['store' => $store] + ); + } + return $this->totalCollector; + } + + /** + * @param int $storeId + * @return \Magento\Quote\Model\Quote\Address\Total\AbstractTotal[] + */ + public function getCollectors($storeId) + { + return $this->getTotalCollector($storeId)->getCollectors(); + } +} diff --git a/app/code/Magento/Quote/Model/Quote/TotalsReader.php b/app/code/Magento/Quote/Model/Quote/TotalsReader.php new file mode 100644 index 0000000000000..b6f15fac53d95 --- /dev/null +++ b/app/code/Magento/Quote/Model/Quote/TotalsReader.php @@ -0,0 +1,99 @@ +totalFactory = $totalFactory; + $this->collectorList = $collectorList; + } + + /** + * @param \Magento\Quote\Model\Quote $quote + * @param array $total + * @return array + */ + public function fetch(\Magento\Quote\Model\Quote $quote, array $total) + { + $output = []; + $total = $this->totalFactory->create('Magento\Quote\Model\Quote\Address\Total')->setData($total); + /** @var ReaderInterface $reader */ + foreach ($this->collectorList->getCollectors($quote->getStoreId()) as $reader) { + $data = $reader->fetch($quote, $total); + if ($data === null || empty($data)) { + continue; + } + + $totalInstance = $this->convert($data); + if (is_array($totalInstance)) { + foreach ($totalInstance as $item) { + $output = $this->merge($item, $output); + } + } else { + $output = $this->merge($totalInstance, $output); + } + } + return $output; + } + + /** + * @param array $total + * @return Total|array + */ + protected function convert($total) + { + if ($total instanceof Total) { + return $total; + } + + if (count(array_column($total, 'code')) > 0) { + $totals = []; + foreach ($total as $item) { + $totals[] = $this->totalFactory->create('Magento\Quote\Model\Quote\Address\Total')->setData($item); + } + return $totals; + } + + return $this->totalFactory->create('Magento\Quote\Model\Quote\Address\Total')->setData($total); + } + + /** + * @param Total $totalInstance + * @param array $output + * @return array + */ + protected function merge($totalInstance, $output) + { + if (array_key_exists($totalInstance->getCode(), $output)) { + $output[$totalInstance->getCode()] = $output[$totalInstance->getCode()]->addData( + $totalInstance->getData() + ); + } else { + $output[$totalInstance->getCode()] = $totalInstance; + } + return $output; + } +} diff --git a/app/code/Magento/Quote/Model/QuoteManagement.php b/app/code/Magento/Quote/Model/QuoteManagement.php index 490e894d7a51f..0accaacd59393 100644 --- a/app/code/Magento/Quote/Model/QuoteManagement.php +++ b/app/code/Magento/Quote/Model/QuoteManagement.php @@ -445,6 +445,7 @@ protected function submitQuote(QuoteEntity $quote, $orderData = []) $order->setCustomerFirstname($quote->getCustomerFirstname()); $order->setCustomerMiddlename($quote->getCustomerMiddlename()); $order->setCustomerLastname($quote->getCustomerLastname()); + $this->eventManager->dispatch( 'sales_model_service_quote_submit_before', [ diff --git a/app/code/Magento/Quote/Model/Shipping.php b/app/code/Magento/Quote/Model/Shipping.php new file mode 100644 index 0000000000000..fef6ab373f5ea --- /dev/null +++ b/app/code/Magento/Quote/Model/Shipping.php @@ -0,0 +1,68 @@ +getData(self::ADDRESS); + } + + /** + * @inheritDoc + */ + public function setAddress(\Magento\Quote\Api\Data\AddressInterface $value) + { + $this->setData(self::ADDRESS, $value); + return $this; + } + + /** + * @inheritDoc + */ + public function getMethod() + { + return $this->getData(self::METHOD); + } + + /** + * @inheritDoc + */ + public function setMethod($value) + { + $this->setData(self::METHOD, $value); + return $this; + } + + /** + * @inheritDoc + */ + public function getExtensionAttributes() + { + return $this->_getExtensionAttributes(); + } + + /** + * @inheritDoc + */ + public function setExtensionAttributes(\Magento\Quote\Api\Data\ShippingExtensionInterface $extensionAttributes) + { + return $this->_setExtensionAttributes($extensionAttributes); + } +} diff --git a/app/code/Magento/Quote/Model/ShippingAddressManagement.php b/app/code/Magento/Quote/Model/ShippingAddressManagement.php index 07f4f61eb198b..d87ea33696b52 100644 --- a/app/code/Magento/Quote/Model/ShippingAddressManagement.php +++ b/app/code/Magento/Quote/Model/ShippingAddressManagement.php @@ -10,7 +10,10 @@ use Psr\Log\LoggerInterface as Logger; use Magento\Quote\Api\ShippingAddressManagementInterface; -/** Quote shipping address write service object. */ +/** + * Quote shipping address write service object. + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ class ShippingAddressManagement implements ShippingAddressManagementInterface { /** @@ -44,25 +47,34 @@ class ShippingAddressManagement implements ShippingAddressManagementInterface */ protected $scopeConfig; + /** + * @var Quote\TotalsCollector + */ + protected $totalsCollector; + /** * @param QuoteRepository $quoteRepository * @param QuoteAddressValidator $addressValidator * @param Logger $logger * @param \Magento\Customer\Api\AddressRepositoryInterface $addressRepository * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + * @param Quote\TotalsCollector $totalsCollector + * */ public function __construct( \Magento\Quote\Model\QuoteRepository $quoteRepository, QuoteAddressValidator $addressValidator, Logger $logger, \Magento\Customer\Api\AddressRepositoryInterface $addressRepository, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, + \Magento\Quote\Model\Quote\TotalsCollector $totalsCollector ) { $this->quoteRepository = $quoteRepository; $this->addressValidator = $addressValidator; $this->logger = $logger; $this->addressRepository = $addressRepository; $this->scopeConfig = $scopeConfig; + $this->totalsCollector = $totalsCollector; } /** @@ -96,7 +108,8 @@ public function assign($cartId, \Magento\Quote\Api\Data\AddressInterface $addres $address->setSaveInAddressBook($saveInAddressBook); $address->setCollectShippingRates(true); try { - $address->collectTotals()->save(); + $this->totalsCollector->collectAddressTotals($quote, $address); + $address->save(); } catch (\Exception $e) { $this->logger->critical($e); throw new InputException(__('Unable to save address. Please, check input data.')); diff --git a/app/code/Magento/Quote/Model/ShippingAssignment.php b/app/code/Magento/Quote/Model/ShippingAssignment.php new file mode 100644 index 0000000000000..51114989ac647 --- /dev/null +++ b/app/code/Magento/Quote/Model/ShippingAssignment.php @@ -0,0 +1,65 @@ +getData(self::SHIPPING); + } + + /** + * @inheritDoc + */ + public function getItems() + { + return $this->getData(self::ITEMS); + } + + /** + * @inheritDoc + */ + public function setShipping(\Magento\Quote\Api\Data\ShippingInterface $value) + { + $this->setData(self::SHIPPING, $value); + return $this; + } + + /** + * @inheritDoc + */ + public function setItems($value) + { + $this->setData(self::ITEMS, $value); + return $this; + } + + /** + * @inheritDoc + */ + public function getExtensionAttributes() + { + return $this->_getExtensionAttributes(); + } + + /** + * @inheritDoc + */ + public function setExtensionAttributes( + \Magento\Quote\Api\Data\ShippingAssignmentExtensionInterface $extensionAttributes + ) { + return $this->_setExtensionAttributes($extensionAttributes); + } +} diff --git a/app/code/Magento/Quote/Model/ShippingMethodManagement.php b/app/code/Magento/Quote/Model/ShippingMethodManagement.php index f54ab223a9d55..88e778e4363bc 100644 --- a/app/code/Magento/Quote/Model/ShippingMethodManagement.php +++ b/app/code/Magento/Quote/Model/ShippingMethodManagement.php @@ -3,9 +3,6 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - namespace Magento\Quote\Model; use Magento\Framework\Exception\InputException; @@ -16,6 +13,7 @@ /** * Shipping method read service. + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ShippingMethodManagement implements ShippingMethodManagementInterface { @@ -40,21 +38,29 @@ class ShippingMethodManagement implements ShippingMethodManagementInterface */ protected $addressRepository; + /** + * @var Quote\TotalsCollector + */ + protected $totalsCollector; + /** * Constructs a shipping method read service object. * - * @param QuoteRepository $quoteRepository Quote repository. - * @param \Magento\Quote\Model\Cart\ShippingMethodConverter $converter Shipping method converter. - * @param \Magento\Customer\Api\AddressRepositoryInterface $addressRepository Customer Address repository + * @param QuoteRepository $quoteRepository + * @param Cart\ShippingMethodConverter $converter + * @param \Magento\Customer\Api\AddressRepositoryInterface $addressRepository + * @param Quote\TotalsCollector $totalsCollector */ public function __construct( QuoteRepository $quoteRepository, Cart\ShippingMethodConverter $converter, - \Magento\Customer\Api\AddressRepositoryInterface $addressRepository + \Magento\Customer\Api\AddressRepositoryInterface $addressRepository, + \Magento\Quote\Model\Quote\TotalsCollector $totalsCollector ) { $this->quoteRepository = $quoteRepository; $this->converter = $converter; $this->addressRepository = $addressRepository; + $this->totalsCollector = $totalsCollector; } /** @@ -121,10 +127,10 @@ public function getList($cartId) * @param string $carrierCode The carrier code. * @param string $methodCode The shipping method code. * @return bool - * @throws \Magento\Framework\Exception\InputException The shipping method is not valid for an empty cart. - * @throws \Magento\Framework\Exception\CouldNotSaveException The shipping method could not be saved. - * @throws \Magento\Framework\Exception\NoSuchEntityException The specified cart contains only virtual products and the shipping method is not applicable. - * @throws \Magento\Framework\Exception\StateException The billing or shipping address is not set. + * @throws InputException The shipping method is not valid for an empty cart. + * @throws CouldNotSaveException The shipping method could not be saved. + * @throws NoSuchEntityException Cart contains only virtual products. Shipping method is not applicable. + * @throws StateException The billing or shipping address is not set. */ public function set($cartId, $carrierCode, $methodCode) { @@ -220,7 +226,7 @@ protected function getEstimatedRates(\Magento\Quote\Model\Quote $quote, $country $shippingAddress->setRegionId($regionId); $shippingAddress->setRegion($region); $shippingAddress->setCollectShippingRates(true); - $shippingAddress->collectTotals(); + $this->totalsCollector->collectAddressTotals($quote, $shippingAddress); $shippingRates = $shippingAddress->getGroupedAllShippingRates(); foreach ($shippingRates as $carrierRates) { foreach ($carrierRates as $rate) { diff --git a/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php b/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php index aad0b9b9cf18d..b8e51993bd0d1 100644 --- a/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php +++ b/app/code/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserver.php @@ -67,21 +67,23 @@ public function __construct( */ public function execute(\Magento\Framework\Event\Observer $observer) { - /** @var \Magento\Quote\Model\Quote\Address $quoteAddress */ - $quoteAddress = $observer->getQuoteAddress(); - + /** @var \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment */ + $shippingAssignment = $observer->getShippingAssignment(); /** @var \Magento\Quote\Model\Quote $quote */ - $quote = $quoteAddress->getQuote(); + $quote = $observer->getQuote(); + /** @var \Magento\Quote\Model\Quote\Address $address */ + $address = $shippingAssignment->getShipping()->getAddress(); + $customer = $quote->getCustomer(); $storeId = $customer->getStoreId(); if ($customer->getDisableAutoGroupChange() - || false == $this->vatValidator->isEnabled($quoteAddress, $storeId) + || false == $this->vatValidator->isEnabled($address, $storeId) ) { return; } - $customerCountryCode = $quoteAddress->getCountryId(); - $customerVatNumber = $quoteAddress->getVatId(); + $customerCountryCode = $address->getCountryId(); + $customerVatNumber = $address->getVatId(); $groupId = null; if (empty($customerVatNumber) || false == $this->customerVat->isCountryInEU($customerCountryCode)) { $groupId = $customer->getId() ? $this->groupManagement->getDefaultGroup( @@ -91,13 +93,13 @@ public function execute(\Magento\Framework\Event\Observer $observer) // Magento always has to emulate group even if customer uses default billing/shipping address $groupId = $this->customerVat->getCustomerGroupIdBasedOnVatNumber( $customerCountryCode, - $this->vatValidator->validate($quoteAddress, $storeId), + $this->vatValidator->validate($address, $storeId), $storeId ); } if ($groupId) { - $quoteAddress->setPrevQuoteCustomerGroupId($quote->getCustomerGroupId()); + $address->setPrevQuoteCustomerGroupId($quote->getCustomerGroupId()); $quote->setCustomerGroupId($groupId); $customer->setGroupId($groupId); $quote->setCustomer($customer); diff --git a/app/code/Magento/Quote/Test/Unit/Model/Cart/CartTotalRepositoryTest.php b/app/code/Magento/Quote/Test/Unit/Model/Cart/CartTotalRepositoryTest.php index 5ac3f50b3fed2..bdc8c37af0cc3 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Cart/CartTotalRepositoryTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Cart/CartTotalRepositoryTest.php @@ -48,7 +48,17 @@ class CartTotalRepositoryTest extends \PHPUnit_Framework_TestCase /** * @var \Magento\Framework\Api\DataObjectHelper|\PHPUnit_Framework_MockObject_MockObject */ - private $dataObjectHelperMock; + protected $dataObjectHelperMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $couponServiceMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $totalsConverterMock; public function setUp() { @@ -60,9 +70,29 @@ public function setUp() '', false ); - $this->quoteMock = $this->getMock('Magento\Quote\Model\Quote', [], [], '', false); + $this->quoteMock = $this->getMock( + 'Magento\Quote\Model\Quote', + [ + 'isVirtual', + 'getShippingAddress', + 'getBillingAddress', + 'getAllVisibleItems', + 'getBaseCurrencyCode', + 'getQuoteCurrencyCode', + 'getItemsQty' + ], + [], + '', + false + ); $this->quoteRepositoryMock = $this->getMock('Magento\Quote\Model\QuoteRepository', [], [], '', false); - $this->addressMock = $this->getMock('Magento\Quote\Model\Quote\Address', [], [], '', false); + $this->addressMock = $this->getMock( + 'Magento\Quote\Model\Quote\Address', + ['getData', 'getTotals'], + [], + '', + false + ); $this->dataObjectHelperMock = $this->getMockBuilder('\Magento\Framework\Api\DataObjectHelper') ->disableOriginalConstructor() ->getMock(); @@ -74,27 +104,31 @@ public function setUp() false ); - $this->model = $this->objectManager->getObject( - '\Magento\Quote\Model\Cart\CartTotalRepository', - [ - 'totalsFactory' => $this->totalsFactoryMock, - 'quoteRepository' => $this->quoteRepositoryMock, - 'dataObjectHelper' => $this->dataObjectHelperMock, - 'converter' => $this->converterMock, - ] + $this->couponServiceMock = $this->getMock('\Magento\Quote\Api\CouponManagementInterface'); + $this->totalsConverterMock = $this->getMock('\Magento\Quote\Model\Cart\TotalsConverter', [], [], '', false); + + $this->model = new \Magento\Quote\Model\Cart\CartTotalRepository( + $this->totalsFactoryMock, + $this->quoteRepositoryMock, + $this->dataObjectHelperMock, + $this->couponServiceMock, + $this->totalsConverterMock, + $this->converterMock ); } - public function testGet() + /** + * @param bool $isVirtual + * @param string $getAddressType + * @dataProvider getDataProvider + */ + public function testGet($isVirtual, $getAddressType) { $cartId = 12; - $itemMock = $this->getMock( - 'Magento\Quote\Model\Quote\Item', - [], - [], - '', - false - ); + $itemsQty = 100; + $coupon = 'coupon'; + $addressTotals = ['address' => 'totals']; + $itemMock = $this->getMock('Magento\Quote\Model\Quote\Item', [], [], '', false); $visibleItems = [ 11 => $itemMock, ]; @@ -102,15 +136,22 @@ public function testGet() 'name' => 'item', 'options' => [ 4 => ['label' => 'justLabel']], ]; - $this->quoteRepositoryMock->expects($this->once())->method('getActive')->with($cartId) - ->will($this->returnValue($this->quoteMock)); - $this->quoteMock->expects($this->once())->method('getShippingAddress')->willReturn($this->addressMock); - $this->addressMock->expects($this->once())->method('getData')->willReturn(['addressData']); - $this->quoteMock->expects($this->once())->method('getData')->willReturn(['quoteData']); - + $currencyCode = 'US'; + + $this->quoteRepositoryMock->expects($this->once()) + ->method('getActive') + ->with($cartId) + ->willReturn($this->quoteMock); + $this->quoteMock->expects($this->once())->method('isVirtual')->willReturn($isVirtual); + $this->quoteMock->expects($this->exactly(2))->method($getAddressType)->willReturn($this->addressMock); $this->quoteMock->expects($this->once())->method('getAllVisibleItems')->willReturn($visibleItems); + $this->quoteMock->expects($this->once())->method('getBaseCurrencyCode')->willReturn($currencyCode); + $this->quoteMock->expects($this->once())->method('getQuoteCurrencyCode')->willReturn($currencyCode); + $this->quoteMock->expects($this->once())->method('getItemsQty')->willReturn($itemsQty); + $this->addressMock->expects($this->any())->method('getData')->willReturn($addressTotals); + $this->addressMock->expects($this->once())->method('getTotals')->willReturn($addressTotals); - $totalsMock = $this->getMock('Magento\Quote\Model\Cart\Totals', ['setItems'], [], '', false); + $totalsMock = $this->getMock('\Magento\Quote\Api\Data\TotalsInterface'); $this->totalsFactoryMock->expects($this->once())->method('create')->willReturn($totalsMock); $this->dataObjectHelperMock->expects($this->once())->method('populateWithArray'); $this->converterMock->expects($this->once()) @@ -118,13 +159,36 @@ public function testGet() ->with($itemMock) ->willReturn($itemArray); - //back in get() - $totalsMock->expects($this->once())->method('setItems')->with( - [ - 11 => $itemArray, - ] - ); + $totalSegmentsMock = $this->getMock('\Magento\Quote\Api\Data\TotalSegmentInterface'); + $this->totalsConverterMock->expects($this->once()) + ->method('process') + ->with($addressTotals) + ->willReturn($totalSegmentsMock); + + $this->couponServiceMock->expects($this->once())->method('get')->with($cartId)->willReturn($coupon); + + $totalsMock->expects($this->once())->method('setItems')->with([11 => $itemArray])->willReturnSelf(); + $totalsMock->expects($this->once())->method('setTotalSegments')->with($totalSegmentsMock)->willReturnSelf(); + $totalsMock->expects($this->once())->method('setCouponCode')->with($coupon)->willReturnSelf(); + $totalsMock->expects($this->once())->method('setGrandTotal')->willReturnSelf(); + $totalsMock->expects($this->once())->method('setItemsQty')->with($itemsQty)->willReturnSelf(); + $totalsMock->expects($this->once())->method('setBaseCurrencyCode')->with($currencyCode)->willReturnSelf(); + $totalsMock->expects($this->once())->method('setQuoteCurrencyCode')->with($currencyCode)->willReturnSelf(); $this->assertEquals($totalsMock, $this->model->get($cartId)); } + + public function getDataProvider() + { + return [ + 'Virtual Quote' => [ + 'isVirtual' => true, + 'getAddressType' => 'getBillingAddress' + ], + 'Non-virtual Quote' => [ + 'isVirtual' => false, + 'getAddressType' => 'getShippingAddress' + ] + ]; + } } diff --git a/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/GrandTest.php b/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/GrandTest.php index 71bef0ac003a4..ce3234668e2ce 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/GrandTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/GrandTest.php @@ -27,18 +27,22 @@ public function testCollect() $grandTotal = 6.4; // 1 + 2 + 3.4 $grandTotalBase = 15.7; // 4 + 5 + 6.7 - $addressMock = $this->getMock( - '\Magento\Quote\Model\Quote\Address', - ['getAllTotalAmounts', 'getAllBaseTotalAmounts', 'setGrandTotal', 'setBaseGrandTotal', '__wakeup'], + $totalMock = $this->getMock( + '\Magento\Quote\Model\Quote\Address\Total', + ['getAllTotalAmounts', 'getAllBaseTotalAmounts', 'setGrandTotal', 'setBaseGrandTotal'], [], '', false ); - $addressMock->expects($this->once())->method('getAllTotalAmounts')->willReturn($totals); - $addressMock->expects($this->once())->method('getAllBaseTotalAmounts')->willReturn($totalsBase); - $addressMock->expects($this->once())->method('setGrandTotal')->with($grandTotal); - $addressMock->expects($this->once())->method('setBaseGrandTotal')->with($grandTotalBase); + $totalMock->expects($this->once())->method('getAllTotalAmounts')->willReturn($totals); + $totalMock->expects($this->once())->method('getAllBaseTotalAmounts')->willReturn($totalsBase); + $totalMock->expects($this->once())->method('setGrandTotal')->with($grandTotal); + $totalMock->expects($this->once())->method('setBaseGrandTotal')->with($grandTotalBase); - $this->model->collect($addressMock); + $this->model->collect( + $this->getMock('\Magento\Quote\Model\Quote', [], [], '', false), + $this->getMock('\Magento\Quote\Api\Data\ShippingAssignmentInterface'), + $totalMock + ); } } diff --git a/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/ShippingTest.php b/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/ShippingTest.php index 2ac71e0e1a1f7..7ea15abed956b 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/ShippingTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/ShippingTest.php @@ -18,59 +18,27 @@ protected function setUp() $this->shippingModel = $objectManager->getObject('Magento\Quote\Model\Quote\Address\Total\Shipping'); } - /** - * @dataProvider fetchDataProvider - */ - public function testFetch($shippingAmount, $shippingDescription, $expectedTotal) + public function testFetch() { - $address = $this->getMock( - 'Magento\Quote\Model\Quote\Address', - ['getShippingAmount', 'getShippingDescription', 'addTotal', '__wakeup'], + $shippingAmount = 100; + $shippingDescription = 100; + $expectedResult = [ + 'code' => 'shipping', + 'value' => 100, + 'title' => __('Shipping & Handling (%1)', $shippingDescription) + ]; + + $quoteMock = $this->getMock('\Magento\Quote\Model\Quote', [], [], '', false); + $totalMock = $this->getMock( + '\Magento\Quote\Model\Quote\Address\Total', + ['getShippingAmount', 'getShippingDescription'], [], '', false ); - $address->expects($this->once())->method('getShippingAmount')->will($this->returnValue($shippingAmount)); - - $address->expects( - $this->once() - )->method( - 'getShippingDescription' - )->will( - $this->returnValue($shippingDescription) - ); - - $address->expects( - $this->once() - )->method( - 'addTotal' - )->with( - $this->equalTo($expectedTotal) - )->will( - $this->returnSelf() - ); - - $this->assertEquals($this->shippingModel, $this->shippingModel->fetch($address)); - } - - public function fetchDataProvider() - { - return [ - [ - 'shipping_amount' => 1, - 'shipping_description' => 'Shipping Method', - 'expected' => [ - 'code' => 'shipping', - 'title' => __('Shipping & Handling (%1)', 'Shipping Method'), - 'value' => 1, - ], - ], - [ - 'shipping_amount' => 1, - 'shipping_description' => '', - 'expected' => ['code' => 'shipping', 'title' => __('Shipping & Handling'), 'value' => 1] - ] - ]; + $totalMock->expects($this->once())->method('getShippingAmount')->willReturn($shippingAmount); + $totalMock->expects($this->once())->method('getShippingDescription')->willReturn($shippingDescription); + $this->assertEquals($expectedResult, $this->shippingModel->fetch($quoteMock, $totalMock)); } } diff --git a/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/SubtotalTest.php b/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/SubtotalTest.php index 05a899032ec8b..a99f923ed26c2 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/SubtotalTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/Total/SubtotalTest.php @@ -34,6 +34,21 @@ protected function setUp() { $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->subtotalModel = $this->objectManager->getObject('Magento\Quote\Model\Quote\Address\Total\Subtotal'); + + $this->stockRegistry = $this->getMock( + 'Magento\CatalogInventory\Model\StockRegistry', + ['getStockItem', '__wakeup'], + [], + '', + false + ); + $this->stockItemMock = $this->getMock( + 'Magento\CatalogInventory\Model\Stock\Item', + ['getIsInStock', '__wakeup'], + [], + '', + false + ); } public function collectDataProvider() @@ -60,28 +75,12 @@ public function collectDataProvider() */ public function testCollect($price, $originalPrice, $itemHasParent, $expectedPrice, $expectedOriginalPrice) { - $this->stockRegistry = $this->getMockBuilder('Magento\CatalogInventory\Model\StockRegistry') - ->disableOriginalConstructor() - ->setMethods(['getStockItem', '__wakeup']) - ->getMock(); - - $this->stockItemMock = $this->getMock( - 'Magento\CatalogInventory\Model\Stock\Item', - ['getIsInStock', '__wakeup'], - [], - '', - false - ); - - $this->stockRegistry->expects($this->any()) - ->method('getStockItem') - ->will($this->returnValue($this->stockItemMock)); + $this->stockRegistry->expects($this->any())->method('getStockItem')->willReturn($this->stockItemMock); $priceCurrency = $this->getMockBuilder('Magento\Framework\Pricing\PriceCurrencyInterface')->getMock(); - $priceCurrency->expects($this->any()) - ->method('convert') - ->willReturn(1231313); - //@todo this is a wrong test and it does not check methods. Any digital value will be correct + // @TODO this is a wrong test and it does not check methods. Any digital value will be correct + $priceCurrency->expects($this->any())->method('convert')->willReturn(1231313); + /** @var \Magento\Quote\Model\Quote\Item|\PHPUnit_Framework_MockObject_MockObject $quoteItem */ $quoteItem = $this->objectManager->getObject( @@ -99,71 +98,71 @@ public function testCollect($price, $originalPrice, $itemHasParent, $expectedPri '', false ); - $address->expects($this->any())->method('getAllItems')->will( - $this->returnValue([$quoteItem]) - ); /** @var \Magento\Catalog\Model\Product|\PHPUnit_Framework_MockObject_MockObject $product */ - $product = $this->getMock( - 'Magento\Catalog\Model\Product', - [], - [], - '', - false - ); + $product = $this->getMock('Magento\Catalog\Model\Product', [], [], '', false); $product->expects($this->any())->method('getPrice')->will($this->returnValue($originalPrice)); + /** @var \Magento\Quote\Model\Quote|\PHPUnit_Framework_MockObject_MockObject $quote */ - $quote = $this->getMock( - 'Magento\Quote\Model\Quote', - [], - [], - '', - false - ); + $quote = $this->getMock('Magento\Quote\Model\Quote', [], [], '', false); $store = $this->objectManager->getObject('Magento\Store\Model\Store'); $store->setCurrentCurrency(''); $store = $this->getMock('Magento\Store\Model\Store', ['getWebsiteId'], [], '', false); - $store->expects($this->any()) - ->method('getWebsiteId') - ->will($this->returnValue(10)); - - $product->expects($this->any()) - ->method('getStore') - ->will($this->returnValue($store)); + $store->expects($this->any())->method('getWebsiteId')->willReturn(10); + $product->expects($this->any())->method('getStore')->willReturn($store); + $product->expects($this->any())->method('isVisibleInCatalog')->will($this->returnValue(true)); $quote->expects($this->any())->method('getStore')->will($this->returnValue($store)); $quoteItem->setProduct($product)->setQuote($quote)->setOriginalCustomPrice($price); - $address->expects($this->any())->method('getAllItems')->will( - $this->returnValue([$quoteItem]) - ); - $address->expects($this->any())->method('getQuote')->will($this->returnValue($quote)); - $product->expects($this->any())->method('isVisibleInCatalog')->will($this->returnValue(true)); - $parentQuoteItem = false; if ($itemHasParent) { - $parentQuoteItem = $this->getMock( - 'Magento\Quote\Model\Quote\Item', - [], - [], - '', - false - ); + $parentQuoteItem = $this->getMock('Magento\Quote\Model\Quote\Item', [], [], '', false); $parentQuoteItem->expects($this->any())->method('getProduct')->will($this->returnValue($product)); } $quoteItem->setParentItem($parentQuoteItem); $priceModel = $this->getMock('\Magento\Catalog\Model\Product\Type\Price', [], [], '', false); - $priceModel->expects($this->any())->method('getChildFinalPrice')->will( - $this->returnValue($price) - ); - $product->expects($this->any())->method('getPriceModel')->will( - $this->returnValue($priceModel) + $priceModel->expects($this->any())->method('getChildFinalPrice')->willReturn($price); + $product->expects($this->any())->method('getPriceModel')->willReturn($priceModel); + $product->expects($this->any())->method('getFinalPrice')->willReturn($price); + + $shipping = $this->getMock('\Magento\Quote\Api\Data\ShippingInterface'); + $shipping->expects($this->exactly(2))->method('getAddress')->willReturn($address); + $shippingAssignmentMock = $this->getMock('\Magento\Quote\Api\Data\ShippingAssignmentInterface'); + $shippingAssignmentMock->expects($this->exactly(2))->method('getShipping')->willReturn($shipping); + $shippingAssignmentMock->expects($this->once())->method('getItems')->willReturn([$quoteItem]); + + $total = $this->getMock( + '\Magento\Quote\Model\Quote\Address\Total', + ['setBaseVirtualAmount', 'setVirtualAmount', 'setTotalQty'], + [], + '', + false ); - $product->expects($this->any())->method('getFinalPrice')->will($this->returnValue($price)); - $this->subtotalModel->collect($address); + $total->expects($this->once())->method('setBaseVirtualAmount')->willReturnSelf(); + $total->expects($this->once())->method('setVirtualAmount')->willReturnSelf(); + $total->expects($this->once())->method('setTotalQty')->with(0)->willReturnSelf(); + + $this->subtotalModel->collect($quote, $shippingAssignmentMock, $total); + $this->assertEquals($expectedPrice, $quoteItem->getPrice()); $this->assertEquals($expectedOriginalPrice, $quoteItem->getBaseOriginalPrice()); } + + public function testFetch() + { + $expectedResult = [ + 'code' => null, + 'title' => __('Subtotal'), + 'value' => 100 + ]; + + $quoteMock = $this->getMock('\Magento\Quote\Model\Quote', [], [], '', false); + $totalMock = $this->getMock('\Magento\Quote\Model\Quote\Address\Total', ['getSubtotal'], [], '', false); + $totalMock->expects($this->once())->method('getSubtotal')->willReturn(100); + + $this->assertEquals($expectedResult, $this->subtotalModel->fetch($quoteMock, $totalMock)); + } } diff --git a/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/TotalTest.php b/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/TotalTest.php new file mode 100644 index 0000000000000..d8729ae606622 --- /dev/null +++ b/app/code/Magento/Quote/Test/Unit/Model/Quote/Address/TotalTest.php @@ -0,0 +1,170 @@ +model = new \Magento\Quote\Model\Quote\Address\Total(); + } + + /** + * @param string $code + * @param float $amount + * @param string $storedCode + * @dataProvider setTotalAmountDataProvider + */ + public function testSetTotalAmount($code, $amount, $storedCode) + { + $result = $this->model->setTotalAmount($code, $amount); + $this->assertArrayHasKey($storedCode, $result); + $this->assertEquals($result[$storedCode], $amount); + $this->assertEquals($this->model->getAllTotalAmounts()[$code], $amount); + $this->assertSame($this->model, $result); + } + + public function setTotalAmountDataProvider() + { + return [ + 'Subtotal' => [ + 'code' => 'subtotal', + 'amount' => 42.42, + 'stored_code' => 'subtotal' + ], + 'Other total' => [ + 'code' => 'other', + 'amount' => 42.17, + 'stored_code' => 'other_amount' + ] + ]; + } + + /** + * @param string $code + * @param float $amount + * @param string $storedCode + * @dataProvider setBaseTotalAmountDataProvider + */ + public function testSetBaseTotalAmount($code, $amount, $storedCode) + { + $result = $this->model->setBaseTotalAmount($code, $amount); + $this->assertArrayHasKey($storedCode, $result); + $this->assertEquals($result[$storedCode], $amount); + $this->assertEquals($this->model->getAllBaseTotalAmounts()[$code], $amount); + $this->assertSame($this->model, $result); + } + + public function setBaseTotalAmountDataProvider() + { + return [ + 'Subtotal' => [ + 'code' => 'subtotal', + 'amount' => 17.42, + 'stored_code' => 'base_subtotal' + ], + 'Other total' => [ + 'code' => 'other', + 'amount' => 42.17, + 'stored_code' => 'base_other_amount' + ] + ]; + } + + /** + * @param float $initialAmount + * @param float $delta + * @param float $updatedAmount + * @dataProvider addTotalAmountDataProvider + */ + public function testAddTotalAmount($initialAmount, $delta, $updatedAmount) + { + $code = 'turbo'; + $this->model->setTotalAmount($code, $initialAmount); + + $this->assertSame($this->model, $this->model->addTotalAmount($code, $delta)); + $this->assertEquals($updatedAmount, $this->model->getTotalAmount($code)); + } + + public function addTotalAmountDataProvider() + { + return [ + 'Zero' => [ + 'initialAmount' => 0, + 'delta' => 42, + 'updatedAmount' => 42 + ], + 'Non-zero' => [ + 'initialAmount' => 20, + 'delta' => 22, + 'updatedAmount' => 42 + ] + ]; + } + + /** + * @param float $initialAmount + * @param float $delta + * @param float $updatedAmount + * @dataProvider addBaseTotalAmountDataProvider + */ + public function testAddBaseTotalAmount($initialAmount, $delta, $updatedAmount) + { + $code = 'turbo'; + $this->model->setBaseTotalAmount($code, $initialAmount); + + $this->assertSame($this->model, $this->model->addBaseTotalAmount($code, $delta)); + $this->assertEquals($updatedAmount, $this->model->getBaseTotalAmount($code)); + } + + public function addBaseTotalAmountDataProvider() + { + return [ + 'Zero' => [ + 'initialAmount' => 0, + 'delta' => 42, + 'updatedAmount' => 42 + ], + 'Non-zero' => [ + 'initialAmount' => 20, + 'delta' => 22, + 'updatedAmount' => 42 + ] + ]; + } + + public function testGetTotalAmount() + { + $code = 'super'; + $amount = 42; + $this->model->setTotalAmount($code, $amount); + $this->assertEquals($amount, $this->model->getTotalAmount($code)); + } + + public function testGetTotalAmountAbsent() + { + $this->assertEquals(0, $this->model->getTotalAmount('mega')); + } + + public function testGetBaseTotalAmount() + { + $code = 'wow'; + $amount = 42; + $this->model->setBaseTotalAmount($code, $amount); + $this->assertEquals($amount, $this->model->getBaseTotalAmount($code)); + } + + public function testGetBaseTotalAmountAbsent() + { + $this->assertEquals(0, $this->model->getBaseTotalAmount('great')); + } +} diff --git a/app/code/Magento/Quote/Test/Unit/Model/Quote/TotalsReaderTest.php b/app/code/Magento/Quote/Test/Unit/Model/Quote/TotalsReaderTest.php new file mode 100644 index 0000000000000..0b496181e03f0 --- /dev/null +++ b/app/code/Magento/Quote/Test/Unit/Model/Quote/TotalsReaderTest.php @@ -0,0 +1,168 @@ +totalFactoryMock = + $this->getMock('Magento\Quote\Model\Quote\Address\TotalFactory', ['create'], [], '', false); + $this->collectionListMock = $this->getMock('Magento\Quote\Model\Quote\TotalsCollectorList', [], [], '', false); + $this->totalMock = $this->getMock('Magento\Quote\Model\Quote\Address\Total', ['setData'], [], '', false); + $this->quoteMock = $this->getMock('\Magento\Quote\Model\Quote', [], [], '', false); + $this->collectorMock = + $this->getMock('Magento\Quote\Model\Quote\Address\Total\AbstractTotal', [], [], '', false); + $this->model = new \Magento\Quote\Model\Quote\TotalsReader( + $this->totalFactoryMock, + $this->collectionListMock + ); + } + + public function testFetch() + { + $total = []; + $storeId = 1; + $testedTotalMock = + $this->getMock('Magento\Quote\Model\Quote\Address\Total', ['setData', 'getCode'], [], '', false); + $expected = ['my_total_type' => $testedTotalMock]; + $data = ['code' => 'my_total_type']; + $this->totalMock->expects($this->once())->method('setData')->with([])->willReturnSelf(); + $this->quoteMock->expects($this->once())->method('getStoreId')->willReturn($storeId); + $this->totalFactoryMock + ->expects($this->at(0)) + ->method('create') + ->willReturn($this->totalMock); + $this->totalFactoryMock->expects($this->at(1))->method('create')->willReturn($testedTotalMock); + $this->collectionListMock + ->expects($this->once()) + ->method('getCollectors') + ->with($storeId)->willReturn([$this->collectorMock]); + $this->collectorMock + ->expects($this->once()) + ->method('fetch') + ->with($this->quoteMock, $this->totalMock) + ->willReturn($data); + $testedTotalMock->expects($this->once())->method('setData')->with($data)->willReturnSelf(); + $testedTotalMock->expects($this->any())->method('getCode')->willReturn('my_total_type'); + $this->assertEquals($expected, $this->model->fetch($this->quoteMock, $total)); + } + + public function testFetchWithEmptyData() + { + $total = []; + $storeId = 1; + $this->totalMock->expects($this->once())->method('setData')->with([])->willReturnSelf(); + $this->quoteMock->expects($this->once())->method('getStoreId')->willReturn($storeId); + $this->totalFactoryMock + ->expects($this->once()) + ->method('create') + ->willReturn($this->totalMock); + $this->collectionListMock + ->expects($this->once()) + ->method('getCollectors') + ->with($storeId)->willReturn([$this->collectorMock]); + $this->collectorMock + ->expects($this->once()) + ->method('fetch') + ->with($this->quoteMock, $this->totalMock) + ->willReturn([]); + $this->assertEquals([], $this->model->fetch($this->quoteMock, $total)); + } + + public function testFetchSeveralCollectors() + { + $total = []; + $storeId = 1; + $firstTotalMock = + $this->getMock('Magento\Quote\Model\Quote\Address\Total', ['setData', 'getCode'], [], '', false); + $secondTotalMock = + $this->getMock('Magento\Quote\Model\Quote\Address\Total', ['setData', 'getCode'], [], '', false); + $expected = ['first_total_type' => $firstTotalMock, 'second_total_type' => $secondTotalMock]; + $data = [['code' => 'first_total_type'], ['code' => 'second_total_type']]; + $this->totalMock->expects($this->once())->method('setData')->with([])->willReturnSelf(); + $this->quoteMock->expects($this->once())->method('getStoreId')->willReturn($storeId); + $this->totalFactoryMock + ->expects($this->at(0)) + ->method('create') + ->willReturn($this->totalMock); + $this->totalFactoryMock->expects($this->at(1))->method('create')->willReturn($firstTotalMock); + $this->totalFactoryMock->expects($this->at(2))->method('create')->willReturn($secondTotalMock); + $this->collectionListMock + ->expects($this->once()) + ->method('getCollectors') + ->with($storeId)->willReturn([$this->collectorMock]); + $this->collectorMock + ->expects($this->once()) + ->method('fetch') + ->with($this->quoteMock, $this->totalMock) + ->willReturn($data); + $firstTotalMock->expects($this->once())->method('setData')->with($data[0])->willReturnSelf(); + $secondTotalMock->expects($this->once())->method('setData')->with($data[1])->willReturnSelf(); + $firstTotalMock->expects($this->any())->method('getCode')->willReturn('first_total_type'); + $secondTotalMock->expects($this->any())->method('getCode')->willReturn('second_total_type'); + $this->assertEquals($expected, $this->model->fetch($this->quoteMock, $total)); + } + + public function testConvert() + { + $total = []; + $storeId = 1; + $testedTotalMock = + $this->getMock('Magento\Quote\Model\Quote\Address\Total', ['setData', 'getCode'], [], '', false); + $expected = ['my_total_type' => $testedTotalMock]; + $this->totalMock->expects($this->once())->method('setData')->with([])->willReturnSelf(); + $this->quoteMock->expects($this->once())->method('getStoreId')->willReturn($storeId); + $this->totalFactoryMock + ->expects($this->once()) + ->method('create') + ->willReturn($this->totalMock); + $this->collectionListMock + ->expects($this->once()) + ->method('getCollectors') + ->with($storeId)->willReturn([$this->collectorMock]); + $this->collectorMock + ->expects($this->once()) + ->method('fetch') + ->with($this->quoteMock, $this->totalMock) + ->willReturn($testedTotalMock); + $testedTotalMock->expects($this->never())->method('setData'); + $testedTotalMock->expects($this->any())->method('getCode')->willReturn('my_total_type'); + $this->assertEquals($expected, $this->model->fetch($this->quoteMock, $total)); + } +} diff --git a/app/code/Magento/Quote/Test/Unit/Model/ShippingAddressManagementTest.php b/app/code/Magento/Quote/Test/Unit/Model/ShippingAddressManagementTest.php index 2fea9a5b9a15c..a62cb7f5598eb 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/ShippingAddressManagementTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/ShippingAddressManagementTest.php @@ -42,6 +42,11 @@ class ShippingAddressManagementTest extends \PHPUnit_Framework_TestCase */ protected $objectManager; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $totalsCollectorMock; + protected function setUp() { $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -67,13 +72,15 @@ protected function setUp() $this->validatorMock = $this->getMock( 'Magento\Quote\Model\QuoteAddressValidator', [], [], '', false ); + $this->totalsCollectorMock = $this->getMock('Magento\Quote\Model\Quote\TotalsCollector', [], [], '', false); $this->service = $this->objectManager->getObject( '\Magento\Quote\Model\ShippingAddressManagement', [ 'quoteRepository' => $this->quoteRepositoryMock, 'addressValidator' => $this->validatorMock, 'logger' => $this->getMock('\Psr\Log\LoggerInterface'), - 'scopeConfig' => $this->scopeConfigMock + 'scopeConfig' => $this->scopeConfigMock, + 'totalsCollector' => $this->totalsCollectorMock ] ); } @@ -110,8 +117,10 @@ public function testSetAddress() $this->validatorMock->expects($this->once())->method('validate') ->with($this->quoteAddressMock) ->will($this->returnValue(true)); - - $this->quoteAddressMock->expects($this->once())->method('collectTotals')->willReturnSelf(); + $this->totalsCollectorMock + ->expects($this->once()) + ->method('collectAddressTotals') + ->with($quoteMock, $this->quoteAddressMock); $this->quoteAddressMock->expects($this->once())->method('save')->willReturnSelf(); $this->quoteAddressMock->expects($this->once())->method('getId')->will($this->returnValue($addressId)); @@ -155,12 +164,16 @@ public function testSetAddressForVirtualProduct() */ public function testSetAddressWithInabilityToSaveQuote() { - $this->quoteAddressMock->expects($this->once())->method('collectTotals')->willReturnSelf(); + $this->quoteAddressMock->expects($this->once())->method('save')->willThrowException( new \Exception('Unable to save address. Please, check input data.') ); $quoteMock = $this->getMock('\Magento\Quote\Model\Quote', [], [], '', false); + $this->totalsCollectorMock + ->expects($this->once()) + ->method('collectAddressTotals') + ->with($quoteMock, $this->quoteAddressMock); $this->quoteRepositoryMock->expects($this->once()) ->method('getActive') ->with('cart867') @@ -180,7 +193,6 @@ public function testSetAddressWithInabilityToSaveQuote() public function testSetAddressWithViolationOfMinimumAmount() { $storeId = 12; - $this->quoteAddressMock->expects($this->once())->method('collectTotals')->willReturnSelf(); $this->quoteAddressMock->expects($this->once())->method('save'); $quoteMock = $this->getMock('\Magento\Quote\Model\Quote', [], [], '', false); @@ -189,7 +201,10 @@ public function testSetAddressWithViolationOfMinimumAmount() $quoteMock->expects($this->once())->method('isVirtual')->will($this->returnValue(false)); $quoteMock->expects($this->once())->method('getShippingAddress')->willReturn($this->quoteAddressMock); $quoteMock->expects($this->any())->method('getStoreId')->will($this->returnValue($storeId)); - + $this->totalsCollectorMock + ->expects($this->once()) + ->method('collectAddressTotals') + ->with($quoteMock, $this->quoteAddressMock); $this->scopeConfigMock->expects($this->once())->method('getValue') ->with('sales/minimum_order/error_message', \Magento\Store\Model\ScopeInterface::SCOPE_STORE, $storeId); diff --git a/app/code/Magento/Quote/Test/Unit/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php b/app/code/Magento/Quote/Test/Unit/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php index e164da38d9998..76055beb08679 100644 --- a/app/code/Magento/Quote/Test/Unit/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php +++ b/app/code/Magento/Quote/Test/Unit/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php @@ -78,11 +78,6 @@ class CollectTotalsObserverTest extends \PHPUnit_Framework_TestCase */ protected $groupInterfaceMock; - /** - * @var \PHPUnit_Framework_MockObject_MockObject - */ - protected $dataObjectHelperMock; - /** * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ @@ -117,7 +112,7 @@ protected function setUp() ); $this->observerMock = $this->getMock( '\Magento\Framework\Event\Observer', - ['getQuoteAddress'], + ['getShippingAssignment', 'getQuote'], [], '', false @@ -163,32 +158,28 @@ protected function setUp() ['getId'] ); - $this->dataObjectHelperMock = $this->getMockBuilder('Magento\Framework\Api\DataObjectHelper') - ->disableOriginalConstructor() - ->getMock(); + $shippingAssignmentMock = $this->getMock('\Magento\Quote\Api\Data\ShippingAssignmentInterface'); + $shippingMock = $this->getMock('\Magento\Quote\Api\Data\ShippingInterface'); + $shippingAssignmentMock->expects($this->once())->method('getShipping')->willReturn($shippingMock); + $shippingMock->expects($this->once())->method('getAddress')->willReturn($this->quoteAddressMock); - $this->observerMock->expects($this->any()) - ->method('getQuoteAddress') - ->will($this->returnValue($this->quoteAddressMock)); - - $this->quoteAddressMock->expects($this->any())->method('getQuote')->will($this->returnValue($this->quoteMock)); + $this->observerMock->expects($this->once()) + ->method('getShippingAssignment') + ->willReturn($shippingAssignmentMock); + $this->observerMock->expects($this->once())->method('getQuote')->willReturn($this->quoteMock); $this->quoteMock->expects($this->any()) ->method('getCustomer') ->will($this->returnValue($this->customerMock)); $this->customerMock->expects($this->any())->method('getStoreId')->will($this->returnValue($this->storeId)); - $this->model = $this->objectManager->getObject( - 'Magento\Quote\Observer\Frontend\Quote\Address\CollectTotalsObserver', - [ - 'customerAddressHelper' => $this->customerAddressMock, - 'customerVat' => $this->customerVatMock, - 'vatValidator' => $this->vatValidatorMock, - 'customerDataFactory' => $this->customerDataFactoryMock, - 'groupManagement' => $this->groupManagementMock, - 'dataObjectHelper' => $this->dataObjectHelperMock, - ] + $this->model = new \Magento\Quote\Observer\Frontend\Quote\Address\CollectTotalsObserver( + $this->customerAddressMock, + $this->customerVatMock, + $this->vatValidatorMock, + $this->customerDataFactoryMock, + $this->groupManagementMock ); } @@ -231,6 +222,9 @@ public function testDispatchWithCustomerCountryNotInEUAndNotLoggedCustomerInGrou $this->returnValue(false) ); + $groupMock = $this->getMockBuilder('Magento\Customer\Api\Data\GroupInterface') + ->disableOriginalConstructor() + ->getMock(); $this->customerMock->expects($this->once())->method('getId')->will($this->returnValue(null)); /** Assertions */ @@ -269,10 +263,6 @@ public function testDispatchWithDefaultCustomerGroupId() ->method('setPrevQuoteCustomerGroupId') ->with('customerGroupId'); $this->quoteMock->expects($this->once())->method('setCustomerGroupId')->with('defaultCustomerGroupId'); - $this->dataObjectHelperMock->expects($this->never()) - ->method('populateWithArray') - ->with($this->customerMock, ['group_id' => 'defaultCustomerGroupId']) - ->will($this->returnSelf()); $this->customerDataFactoryMock->expects($this->any()) ->method('create') ->willReturn($this->customerMock); @@ -324,10 +314,6 @@ public function testDispatchWithCustomerCountryInEU() $this->quoteMock->expects($this->once())->method('setCustomerGroupId')->with('customerGroupId'); $this->quoteMock->expects($this->once())->method('setCustomer')->with($this->customerMock); - $this->dataObjectHelperMock->expects($this->never()) - ->method('populateWithArray') - ->with($this->customerMock, ['group_id' => 'customerGroupId']) - ->will($this->returnSelf()); $this->customerDataFactoryMock->expects($this->any()) ->method('create') ->willReturn($this->customerMock); diff --git a/app/code/Magento/Quote/etc/di.xml b/app/code/Magento/Quote/etc/di.xml index 3b10c9cdd3993..278663b7f0ff0 100644 --- a/app/code/Magento/Quote/etc/di.xml +++ b/app/code/Magento/Quote/etc/di.xml @@ -39,6 +39,7 @@ + @@ -82,5 +83,6 @@ - + + diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Grandtotal.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Grandtotal.php index 67211ec745a2e..ab06f423eb4d5 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Grandtotal.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Grandtotal.php @@ -70,7 +70,7 @@ public function includeTax() */ public function getTotalExclTax() { - $excl = $this->getTotal()->getAddress()->getGrandTotal() - $this->getTotal()->getAddress()->getTaxAmount(); + $excl = $this->getTotals()['grand_total']->getValue() - $this->getTotals()['tax']->getValue(); $excl = max($excl, 0); return $excl; } diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Shipping.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Shipping.php index 476a33c185b0c..fcd3ecd29903e 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Shipping.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Totals/Shipping.php @@ -80,8 +80,7 @@ public function displayIncludeTax() */ public function getShippingIncludeTax() { - return $this->getTotal()->getAddress()->getShippingAmount() + - $this->getTotal()->getAddress()->getShippingTaxAmount(); + return $this->getTotal()->getShippingInclTax(); } /** @@ -91,7 +90,7 @@ public function getShippingIncludeTax() */ public function getShippingExcludeTax() { - return $this->getTotal()->getAddress()->getShippingAmount(); + return $this->getTotal()->getValue(); } /** @@ -103,7 +102,7 @@ public function getIncludeTaxLabel() { return __( 'Shipping Incl. Tax (%1)', - $this->escapeHtml($this->getTotal()->getAddress()->getShippingDescription()) + $this->escapeHtml($this->getQuote()->getShippingAddress()->getShippingDescription()) ); } @@ -116,7 +115,7 @@ public function getExcludeTaxLabel() { return __( 'Shipping Excl. Tax (%1)', - $this->escapeHtml($this->getTotal()->getAddress()->getShippingDescription()) + $this->escapeHtml($this->getQuote()->getShippingAddress()->getShippingDescription()) ); } } diff --git a/app/code/Magento/Sales/Model/Observer/Frontend/Quote/RestoreCustomerGroupId.php b/app/code/Magento/Sales/Model/Observer/Frontend/Quote/RestoreCustomerGroupId.php index 3ffee413a283e..1384cfecce1e1 100644 --- a/app/code/Magento/Sales/Model/Observer/Frontend/Quote/RestoreCustomerGroupId.php +++ b/app/code/Magento/Sales/Model/Observer/Frontend/Quote/RestoreCustomerGroupId.php @@ -35,14 +35,19 @@ public function __construct(CustomerAddress $customerAddressHelper) */ public function execute($observer) { - $quoteAddress = $observer->getQuoteAddress(); + /** @var \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment */ + $shippingAssignment = $observer->getEvent()->getShippingAssignment(); + /** @var \Magento\Quote\Model\Quote $quote */ + $quote = $observer->getEvent()->getQuote(); + + $address = $shippingAssignment->getShipping()->getAddress(); $configAddressType = $this->customerAddressHelper->getTaxCalculationAddressType(); // Restore initial customer group ID in quote only if VAT is calculated based on shipping address - if ($quoteAddress->hasPrevQuoteCustomerGroupId() && + if ($address->hasPrevQuoteCustomerGroupId() && $configAddressType == \Magento\Customer\Model\Address\AbstractAddress::TYPE_SHIPPING ) { - $quoteAddress->getQuote()->setCustomerGroupId($quoteAddress->getPrevQuoteCustomerGroupId()); - $quoteAddress->unsPrevQuoteCustomerGroupId(); + $quote->setCustomerGroupId($address->getPrevQuoteCustomerGroupId()); + $address->unsPrevQuoteCustomerGroupId(); } } } diff --git a/app/code/Magento/Sales/Test/Unit/Model/Observer/Frontend/Quote/RestoreCustomerGroupIdTest.php b/app/code/Magento/Sales/Test/Unit/Model/Observer/Frontend/Quote/RestoreCustomerGroupIdTest.php index 205910c9ca93e..48ae41556ae87 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Observer/Frontend/Quote/RestoreCustomerGroupIdTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Observer/Frontend/Quote/RestoreCustomerGroupIdTest.php @@ -24,13 +24,8 @@ class RestoreCustomerGroupIdTest extends \PHPUnit_Framework_TestCase protected function setUp() { - $this->customerAddressHelperMock = $this->getMockBuilder('Magento\Customer\Helper\Address') - ->disableOriginalConstructor() - ->getMock(); - - $this->quote = new RestoreCustomerGroupId( - $this->customerAddressHelperMock - ); + $this->customerAddressHelperMock = $this->getMock('\Magento\Customer\Helper\Address', [], [], '', false); + $this->quote = new RestoreCustomerGroupId($this->customerAddressHelperMock); } /** @@ -39,20 +34,27 @@ protected function setUp() */ public function testExecute($configAddressType) { + $eventMock = $this->getMock('\Magento\Framework\Event', ['getShippingAssignment', 'getQuote'], [], '', false); + $observer = $this->getMock('Magento\Framework\Event\Observer', ['getEvent'], [], '', false); + $observer->expects($this->exactly(2))->method('getEvent')->willReturn($eventMock); + + $shippingAssignmentMock = $this->getMock('\Magento\Quote\Api\Data\ShippingAssignmentInterface'); + $quoteMock = $this->getMock('\Magento\Quote\Model\Quote', [], [], '', false); + + $eventMock->expects($this->once())->method('getShippingAssignment')->willReturn($shippingAssignmentMock); + $eventMock->expects($this->once())->method('getQuote')->willReturn($quoteMock); + + $shippingMock = $this->getMock('\Magento\Quote\Api\Data\ShippingInterface'); + $shippingAssignmentMock->expects($this->once())->method('getShipping')->willReturn($shippingMock); + $quoteAddress = $this->getMock( - 'Magento\Quote\Model\Quote\Address', - [ - 'getQuote', 'setCustomerGroupId', 'getPrevQuoteCustomerGroupId', - 'unsPrevQuoteCustomerGroupId', 'hasPrevQuoteCustomerGroupId' - ], + '\Magento\Quote\Model\Quote\Address', + ['getPrevQuoteCustomerGroupId', 'unsPrevQuoteCustomerGroupId', 'hasPrevQuoteCustomerGroupId'], [], '', false ); - $observer = $this->getMock('Magento\Framework\Event\Observer', ['getQuoteAddress'], [], '', false); - $observer->expects($this->once()) - ->method('getQuoteAddress') - ->will($this->returnValue($quoteAddress)); + $shippingMock->expects($this->once())->method('getAddress')->willReturn($quoteAddress); $this->customerAddressHelperMock->expects($this->once()) ->method('getTaxCalculationAddressType') @@ -64,7 +66,7 @@ public function testExecute($configAddressType) $quoteAddress->expects($this->any())->method('getQuote'); $quoteAddress->expects($this->any())->method('unsPrevQuoteCustomerGroupId'); - $this->assertNull($this->quote->execute($observer)); + $this->quote->execute($observer); } public function restoreCustomerGroupIdDataProvider() diff --git a/app/code/Magento/SalesRule/Model/Quote/Discount.php b/app/code/Magento/SalesRule/Model/Quote/Discount.php index d108bb891e6ec..80356b4ef24e2 100644 --- a/app/code/Magento/SalesRule/Model/Quote/Discount.php +++ b/app/code/Magento/SalesRule/Model/Quote/Discount.php @@ -5,10 +5,6 @@ */ namespace Magento\SalesRule\Model\Quote; -use Magento\Framework\Pricing\PriceCurrencyInterface; -use Magento\Quote\Model\Quote\Address; -use Magento\Quote\Model\Quote\Item\AbstractItem; - class Discount extends \Magento\Quote\Model\Quote\Address\Total\AbstractTotal { /** @@ -16,22 +12,22 @@ class Discount extends \Magento\Quote\Model\Quote\Address\Total\AbstractTotal * * @var \Magento\SalesRule\Model\Validator */ - protected $_calculator; + protected $calculator; /** * Core event manager proxy * * @var \Magento\Framework\Event\ManagerInterface */ - protected $_eventManager = null; + protected $eventManager = null; /** * @var \Magento\Store\Model\StoreManagerInterface */ - protected $_storeManager; + protected $storeManager; /** - * @var PriceCurrencyInterface + * @var \Magento\Framework\Pricing\PriceCurrencyInterface */ protected $priceCurrency; @@ -39,36 +35,42 @@ class Discount extends \Magento\Quote\Model\Quote\Address\Total\AbstractTotal * @param \Magento\Framework\Event\ManagerInterface $eventManager * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Magento\SalesRule\Model\Validator $validator - * @param PriceCurrencyInterface $priceCurrency + * @param \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency */ public function __construct( \Magento\Framework\Event\ManagerInterface $eventManager, \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\SalesRule\Model\Validator $validator, - PriceCurrencyInterface $priceCurrency + \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency ) { - $this->_eventManager = $eventManager; $this->setCode('discount'); - $this->_calculator = $validator; - $this->_storeManager = $storeManager; + $this->eventManager = $eventManager; + $this->calculator = $validator; + $this->storeManager = $storeManager; $this->priceCurrency = $priceCurrency; } /** * Collect address discount amount * - * @param Address $address + * @param \Magento\Quote\Model\Quote $quote + * @param \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment + * @param \Magento\Quote\Model\Quote\Address\Total $total * @return $this * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ - public function collect(Address $address) - { - parent::collect($address); - $quote = $address->getQuote(); - $store = $this->_storeManager->getStore($quote->getStoreId()); - $this->_calculator->reset($address); + public function collect( + \Magento\Quote\Model\Quote $quote, + \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment, + \Magento\Quote\Model\Quote\Address\Total $total + ) { + parent::collect($quote, $shippingAssignment, $total); - $items = $this->_getAddressItems($address); + $store = $this->storeManager->getStore($quote->getStoreId()); + $address = $shippingAssignment->getShipping()->getAddress(); + $this->calculator->reset($address); + + $items = $shippingAssignment->getItems(); if (!count($items)) { return $this; } @@ -79,15 +81,15 @@ public function collect(Address $address) 'coupon_code' => $quote->getCouponCode(), ]; - $this->_calculator->init($store->getWebsiteId(), $quote->getCustomerGroupId(), $quote->getCouponCode()); - $this->_calculator->initTotals($items, $address); + $this->calculator->init($store->getWebsiteId(), $quote->getCustomerGroupId(), $quote->getCouponCode()); + $this->calculator->initTotals($items, $address); $address->setDiscountDescription([]); + $items = $this->calculator->sortItemsByPriority($items); - $items = $this->_calculator->sortItemsByPriority($items); /** @var \Magento\Quote\Model\Quote\Item $item */ foreach ($items as $item) { - if ($item->getNoDiscount() || !$this->_calculator->canApplyDiscount($item)) { + if ($item->getNoDiscount() || !$this->calculator->canApplyDiscount($item)) { $item->setDiscountAmount(0); $item->setBaseDiscountAmount(0); @@ -106,58 +108,61 @@ public function collect(Address $address) } $eventArgs['item'] = $item; - $this->_eventManager->dispatch('sales_quote_address_discount_item', $eventArgs); + $this->eventManager->dispatch('sales_quote_address_discount_item', $eventArgs); if ($item->getHasChildren() && $item->isChildrenCalculated()) { - $this->_calculator->process($item); + $this->calculator->process($item); $this->distributeDiscount($item); foreach ($item->getChildren() as $child) { $eventArgs['item'] = $child; - $this->_eventManager->dispatch('sales_quote_address_discount_item', $eventArgs); - - $this->_aggregateItemDiscount($child); + $this->eventManager->dispatch('sales_quote_address_discount_item', $eventArgs); + $this->aggregateItemDiscount($child, $total); } } else { - $this->_calculator->process($item); - $this->_aggregateItemDiscount($item); + $this->calculator->process($item); + $this->aggregateItemDiscount($item, $total); } } - /** - * Process shipping amount discount - */ + /** Process shipping amount discount */ $address->setShippingDiscountAmount(0); $address->setBaseShippingDiscountAmount(0); if ($address->getShippingAmount()) { - $this->_calculator->processShippingAmount($address); - $this->_addAmount(-$address->getShippingDiscountAmount()); - $this->_addBaseAmount(-$address->getBaseShippingDiscountAmount()); + $this->calculator->processShippingAmount($address); + $total->addTotalAmount($this->getCode(), -$address->getShippingDiscountAmount()); + $total->addBaseTotalAmount($this->getCode(), -$address->getBaseShippingDiscountAmount()); } - $this->_calculator->prepareDescription($address); + $this->calculator->prepareDescription($address); + $total->setDiscountDescription($address->getDiscountDescription()); + $total->setSubtotalWithDiscount($total->getSubtotal() + $total->getDiscountAmount()); + $total->setBaseSubtotalWithDiscount($total->getBaseSubtotal() + $total->getBaseDiscountAmount()); return $this; } /** - * Aggregate item discount information to address data and related properties + * Aggregate item discount information to total data and related properties * - * @param AbstractItem $item + * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item + * @param \Magento\Quote\Model\Quote\Address\Total $total * @return $this */ - protected function _aggregateItemDiscount($item) - { - $this->_addAmount(-$item->getDiscountAmount()); - $this->_addBaseAmount(-$item->getBaseDiscountAmount()); + protected function aggregateItemDiscount( + \Magento\Quote\Model\Quote\Item\AbstractItem $item, + \Magento\Quote\Model\Quote\Address\Total $total + ) { + $total->addTotalAmount($this->getCode(), -$item->getDiscountAmount()); + $total->addBaseTotalAmount($this->getCode(), -$item->getBaseDiscountAmount()); return $this; } /** * Distribute discount at parent item to children items * - * @param AbstractItem $item + * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item * @return $this */ - protected function distributeDiscount(AbstractItem $item) + protected function distributeDiscount(\Magento\Quote\Model\Quote\Item\AbstractItem $item) { $parentBaseRowTotal = $item->getBaseRowTotal(); $keys = [ @@ -193,21 +198,23 @@ protected function distributeDiscount(AbstractItem $item) /** * Add discount total information to address * - * @param Address $address - * @return $this + * @param \Magento\Quote\Model\Quote $quote + * @param \Magento\Quote\Model\Quote\Address\Total $total + * @return array|null */ - public function fetch(Address $address) + public function fetch(\Magento\Quote\Model\Quote $quote, \Magento\Quote\Model\Quote\Address\Total $total) { - $amount = $address->getDiscountAmount(); + $result = null; + $amount = $total->getDiscountAmount(); if ($amount != 0) { - $description = $address->getDiscountDescription(); - $title = __('Discount'); - if (strlen($description)) { - $title = __('Discount (%1)', $description); - } - $address->addTotal(['code' => $this->getCode(), 'title' => $title, 'value' => $amount]); + $description = $total->getDiscountDescription(); + $result = [ + 'code' => $this->getCode(), + 'title' => strlen($description) ? __('Discount (%1)', $description) : __('Discount'), + 'value' => $amount + ]; } - return $this; + return $result; } } diff --git a/app/code/Magento/SalesRule/Test/Unit/Model/Quote/DiscountTest.php b/app/code/Magento/SalesRule/Test/Unit/Model/Quote/DiscountTest.php index ef0d91ce32f35..0d209edfb7ff7 100644 --- a/app/code/Magento/SalesRule/Test/Unit/Model/Quote/DiscountTest.php +++ b/app/code/Magento/SalesRule/Test/Unit/Model/Quote/DiscountTest.php @@ -37,12 +37,20 @@ class DiscountTest extends \PHPUnit_Framework_TestCase */ protected $eventManagerMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $shippingAssignmentMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $addressMock; + public function setUp() { $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->storeManagerMock = $this->getMockBuilder('Magento\Store\Model\StoreManager') - ->disableOriginalConstructor() - ->getMock(); + $this->storeManagerMock = $this->getMock('Magento\Store\Model\StoreManager', [], [], '', false); $this->validatorMock = $this->getMockBuilder('Magento\SalesRule\Model\Validator') ->disableOriginalConstructor() ->setMethods( @@ -60,12 +68,8 @@ public function setUp() ] ) ->getMock(); - $this->eventManagerMock = $this->getMockBuilder('Magento\Framework\Event\Manager') - ->disableOriginalConstructor() - ->getMock(); - - $priceCurrencyMock = $this->getMockBuilder('Magento\Framework\Pricing\PriceCurrencyInterface') - ->getMock(); + $this->eventManagerMock = $this->getMock('Magento\Framework\Event\Manager', [], [], '', false); + $priceCurrencyMock = $this->getMock('Magento\Framework\Pricing\PriceCurrencyInterface'); $priceCurrencyMock->expects($this->any()) ->method('round') ->will($this->returnCallback( @@ -74,6 +78,19 @@ function ($argument) { } )); + $this->addressMock = $this->getMock( + '\Magento\Quote\Model\Quote\Address', + ['getQuote', 'getAllItems', 'getShippingAmount', '__wakeup'], + [], + '', + false + ); + + $shipping = $this->getMock('\Magento\Quote\Api\Data\ShippingInterface'); + $shipping->expects($this->any())->method('getAddress')->willReturn($this->addressMock); + $this->shippingAssignmentMock = $this->getMock('\Magento\Quote\Api\Data\ShippingAssignmentInterface'); + $this->shippingAssignmentMock->expects($this->any())->method('getShipping')->willReturn($shipping); + /** @var \Magento\SalesRule\Model\Quote\Discount $discount */ $this->discount = $this->objectManager->getObject( 'Magento\SalesRule\Model\Quote\Discount', @@ -88,98 +105,58 @@ function ($argument) { public function testCollectItemNoDiscount() { - $itemNoDiscount = $this->getMockBuilder('Magento\Quote\Model\Quote\Item') - ->disableOriginalConstructor() - ->setMethods(['getNoDiscount', '__wakeup']) - ->getMock(); - $itemNoDiscount->expects($this->once()) - ->method('getNoDiscount') - ->willReturn(true); + $itemNoDiscount = $this->getMock( + 'Magento\Quote\Model\Quote\Item', + ['getNoDiscount', '__wakeup'], + [], + '', + false + ); + $itemNoDiscount->expects($this->once())->method('getNoDiscount')->willReturn(true); + $this->validatorMock->expects($this->any())->method('sortItemsByPriority')->willReturnArgument(0); + $storeMock = $this->getMock('Magento\Store\Model\Store', ['getStore', '__wakeup'], [], '', false); + $this->storeManagerMock->expects($this->any())->method('getStore')->willReturn($storeMock); + $quoteMock = $this->getMock('Magento\Quote\Model\Quote', [], [], '', false); + $this->addressMock->expects($this->any())->method('getQuote')->willReturn($quoteMock); + $this->shippingAssignmentMock->expects($this->any())->method('getItems')->willReturn([$itemNoDiscount]); + $this->addressMock->expects($this->any())->method('getShippingAmount')->willReturn(true); - $this->validatorMock->expects($this->any()) - ->method('sortItemsByPriority') - ->willReturnArgument(0); - - $storeMock = $this->getMockBuilder('Magento\Store\Model\Store') - ->disableOriginalConstructor() - ->setMethods(['getStore', '__wakeup']) - ->getMock(); - $this->storeManagerMock->expects($this->any()) - ->method('getStore') - ->willReturn($storeMock); - - $quoteMock = $this->getMockBuilder('Magento\Quote\Model\Quote') - ->disableOriginalConstructor() - ->getMock(); - $addressMock = $this->getMockBuilder('Magento\Quote\Model\Quote\Address') - ->disableOriginalConstructor() - ->setMethods(['getQuote', 'getAllItems', 'getShippingAmount', '__wakeup']) - ->getMock(); - $addressMock->expects($this->any()) - ->method('getQuote') - ->willReturn($quoteMock); - $addressMock->expects($this->any()) - ->method('getAllItems') - ->willReturn([$itemNoDiscount]); - $addressMock->expects($this->any()) - ->method('getShippingAmount') - ->willReturn(true); + $totalMock = $this->getMock('\Magento\Quote\Model\Quote\Address\Total', [], [], '', false); $this->assertInstanceOf( 'Magento\SalesRule\Model\Quote\Discount', - $this->discount->collect($addressMock) + $this->discount->collect($quoteMock, $this->shippingAssignmentMock, $totalMock) ); } public function testCollectItemHasParent() { - $itemWithParentId = $this->getMockBuilder('Magento\Quote\Model\Quote\Item') - ->disableOriginalConstructor() - ->setMethods(['getNoDiscount', 'getParentItem', '__wakeup']) - ->getMock(); - $itemWithParentId->expects($this->once()) - ->method('getNoDiscount') - ->willReturn(false); - $itemWithParentId->expects($this->once()) - ->method('getParentItem') - ->willReturn(true); + $itemWithParentId = $this->getMock( + '\Magento\Quote\Model\Quote\Item', + ['getNoDiscount', 'getParentItem', '__wakeup'], + [], + '', + false + ); + $itemWithParentId->expects($this->once())->method('getNoDiscount')->willReturn(false); + $itemWithParentId->expects($this->once())->method('getParentItem')->willReturn(true); - $this->validatorMock->expects($this->any()) - ->method('canApplyDiscount') - ->willReturn(true); + $this->validatorMock->expects($this->any())->method('canApplyDiscount')->willReturn(true); + $this->validatorMock->expects($this->any())->method('sortItemsByPriority')->willReturnArgument(0); - $this->validatorMock->expects($this->any()) - ->method('sortItemsByPriority') - ->willReturnArgument(0); + $storeMock = $this->getMock('\Magento\Store\Model\Store', ['getStore', '__wakeup'], [], '', false); + $this->storeManagerMock->expects($this->any())->method('getStore')->willReturn($storeMock); - $storeMock = $this->getMockBuilder('Magento\Store\Model\Store') - ->disableOriginalConstructor() - ->setMethods(['getStore', '__wakeup']) - ->getMock(); - $this->storeManagerMock->expects($this->any()) - ->method('getStore') - ->willReturn($storeMock); + $quoteMock = $this->getMock('\Magento\Quote\Model\Quote', [], [], '', false); - $quoteMock = $this->getMockBuilder('Magento\Quote\Model\Quote') - ->disableOriginalConstructor() - ->getMock(); - $addressMock = $this->getMockBuilder('Magento\Quote\Model\Quote\Address') - ->disableOriginalConstructor() - ->setMethods(['getQuote', 'getAllItems', 'getShippingAmount', '__wakeup']) - ->getMock(); - $addressMock->expects($this->any()) - ->method('getQuote') - ->willReturn($quoteMock); - $addressMock->expects($this->any()) - ->method('getAllItems') - ->willReturn([$itemWithParentId]); - $addressMock->expects($this->any()) - ->method('getShippingAmount') - ->willReturn(true); + $this->addressMock->expects($this->any())->method('getQuote')->willReturn($quoteMock); + $this->addressMock->expects($this->any())->method('getShippingAmount')->willReturn(true); + $this->shippingAssignmentMock->expects($this->any())->method('getItems')->willReturn([$itemWithParentId]); + $totalMock = $this->getMock('\Magento\Quote\Model\Quote\Address\Total', [], [], '', false); $this->assertInstanceOf( 'Magento\SalesRule\Model\Quote\Discount', - $this->discount->collect($addressMock) + $this->discount->collect($quoteMock, $this->shippingAssignmentMock, $totalMock) ); } @@ -190,7 +167,8 @@ public function testCollectItemHasChildren($childItemData, $parentData, $expecte { $childItems = []; foreach ($childItemData as $itemId => $itemData) { - $childItems[$itemId] = new MagentoObject($itemData); + $item = $this->objectManager->getObject('Magento\Quote\Model\Quote\Item')->setData($itemData); + $childItems[$itemId] = $item; } $itemWithChildren = $this->getMockBuilder('Magento\Quote\Model\Quote\Item') @@ -206,64 +184,37 @@ public function testCollectItemHasChildren($childItemData, $parentData, $expecte ] ) ->getMock(); - $itemWithChildren->expects($this->once()) - ->method('getNoDiscount') - ->willReturn(false); - $itemWithChildren->expects($this->once()) - ->method('getParentItem') - ->willReturn(false); - $itemWithChildren->expects($this->once()) - ->method('getHasChildren') - ->willReturn(true); - $itemWithChildren->expects($this->once()) - ->method('isChildrenCalculated') - ->willReturn(true); - $itemWithChildren->expects($this->any()) - ->method('getChildren') - ->willReturn($childItems); + $itemWithChildren->expects($this->once())->method('getNoDiscount')->willReturn(false); + $itemWithChildren->expects($this->once())->method('getParentItem')->willReturn(false); + $itemWithChildren->expects($this->once())->method('getHasChildren')->willReturn(true); + $itemWithChildren->expects($this->once())->method('isChildrenCalculated')->willReturn(true); + $itemWithChildren->expects($this->any())->method('getChildren')->willReturn($childItems); foreach ($parentData as $key => $value) { $itemWithChildren->setData($key, $value); } - $this->validatorMock->expects($this->any()) - ->method('canApplyDiscount') - ->willReturn(true); - - $this->validatorMock->expects($this->any()) - ->method('sortItemsByPriority') - ->willReturnArgument(0); - $this->validatorMock->expects($this->any()) - ->method('canApplyRules') - ->willReturn(true); + $this->validatorMock->expects($this->any())->method('canApplyDiscount')->willReturn(true); + $this->validatorMock->expects($this->any())->method('sortItemsByPriority')->willReturnArgument(0); + $this->validatorMock->expects($this->any())->method('canApplyRules')->willReturn(true); $storeMock = $this->getMockBuilder('Magento\Store\Model\Store') ->disableOriginalConstructor() ->setMethods(['getStore', '__wakeup']) ->getMock(); - $this->storeManagerMock->expects($this->any()) - ->method('getStore') - ->willReturn($storeMock); + $this->storeManagerMock->expects($this->any())->method('getStore')->willReturn($storeMock); $quoteMock = $this->getMockBuilder('Magento\Quote\Model\Quote') ->disableOriginalConstructor() ->getMock(); - $addressMock = $this->getMockBuilder('Magento\Quote\Model\Quote\Address') - ->disableOriginalConstructor() - ->setMethods(['getQuote', 'getAllItems', 'getShippingAmount', '__wakeup']) - ->getMock(); - $addressMock->expects($this->any()) - ->method('getQuote') - ->willReturn($quoteMock); - $addressMock->expects($this->any()) - ->method('getAllItems') - ->willReturn([$itemWithChildren]); - $addressMock->expects($this->any()) - ->method('getShippingAmount') - ->willReturn(true); + $this->addressMock->expects($this->any())->method('getQuote')->willReturn($quoteMock); + $this->addressMock->expects($this->any())->method('getShippingAmount')->willReturn(true); + + $this->shippingAssignmentMock->expects($this->any())->method('getItems')->willReturn([$itemWithChildren]); + $totalMock = $this->getMock('\Magento\Quote\Model\Quote\Address\Total', [], [], '', false); $this->assertInstanceOf( 'Magento\SalesRule\Model\Quote\Discount', - $this->discount->collect($addressMock) + $this->discount->collect($quoteMock, $this->shippingAssignmentMock, $totalMock) ); foreach ($expectedChildData as $itemId => $expectedItemData) { @@ -337,71 +288,52 @@ public function testCollectItemHasNoChildren() ] ) ->getMock(); - $itemWithChildren->expects($this->once()) - ->method('getNoDiscount') - ->willReturn(false); - $itemWithChildren->expects($this->once()) - ->method('getParentItem') - ->willReturn(false); - $itemWithChildren->expects($this->once()) - ->method('getHasChildren') - ->willReturn(false); - - $this->validatorMock->expects($this->any()) - ->method('canApplyDiscount') - ->willReturn(true); - - $this->validatorMock->expects($this->any()) - ->method('sortItemsByPriority') - ->willReturnArgument(0); + $itemWithChildren->expects($this->once())->method('getNoDiscount')->willReturn(false); + $itemWithChildren->expects($this->once())->method('getParentItem')->willReturn(false); + $itemWithChildren->expects($this->once())->method('getHasChildren')->willReturn(false); + + $this->validatorMock->expects($this->any())->method('canApplyDiscount')->willReturn(true); + $this->validatorMock->expects($this->any())->method('sortItemsByPriority')->willReturnArgument(0); $storeMock = $this->getMockBuilder('Magento\Store\Model\Store') ->disableOriginalConstructor() ->setMethods(['getStore', '__wakeup']) ->getMock(); - $this->storeManagerMock->expects($this->any()) - ->method('getStore') - ->willReturn($storeMock); + $this->storeManagerMock->expects($this->any())->method('getStore')->willReturn($storeMock); - $quoteMock = $this->getMockBuilder('Magento\Quote\Model\Quote') - ->disableOriginalConstructor() - ->getMock(); - $addressMock = $this->getMockBuilder('Magento\Quote\Model\Quote\Address') - ->disableOriginalConstructor() - ->setMethods(['getQuote', 'getAllItems', 'getShippingAmount', '__wakeup']) - ->getMock(); - $addressMock->expects($this->any()) - ->method('getQuote') - ->willReturn($quoteMock); - $addressMock->expects($this->any()) - ->method('getAllItems') - ->willReturn([$itemWithChildren]); - $addressMock->expects($this->any()) - ->method('getShippingAmount') - ->willReturn(true); + $quoteMock = $this->getMockBuilder('Magento\Quote\Model\Quote')->disableOriginalConstructor()->getMock(); + $this->addressMock->expects($this->any())->method('getQuote')->willReturn($quoteMock); + $this->addressMock->expects($this->any())->method('getShippingAmount')->willReturn(true); + $this->shippingAssignmentMock->expects($this->any())->method('getItems')->willReturn([$itemWithChildren]); + $totalMock = $this->getMock('\Magento\Quote\Model\Quote\Address\Total', [], [], '', false); $this->assertInstanceOf( 'Magento\SalesRule\Model\Quote\Discount', - $this->discount->collect($addressMock) + $this->discount->collect($quoteMock, $this->shippingAssignmentMock, $totalMock) ); } public function testFetch() { - $addressMock = $this->getMockBuilder('Magento\Quote\Model\Quote\Address') - ->disableOriginalConstructor() - ->setMethods(['getDiscountAmount', 'getDiscountDescription', 'addTotal', '__wakeup']) - ->getMock(); - $addressMock->expects($this->once()) - ->method('getDiscountAmount') - ->willReturn(10); - $addressMock->expects($this->once()) - ->method('getDiscountDescription') - ->willReturn('test description'); + $discountAmount = 100; + $discountDescription = 100; + $expectedResult = [ + 'code' => 'discount', + 'value' => 100, + 'title' => __('Discount (%1)', $discountDescription) + ]; - $this->assertInstanceOf( - 'Magento\SalesRule\Model\Quote\Discount', - $this->discount->fetch($addressMock) + $quoteMock = $this->getMock('\Magento\Quote\Model\Quote', [], [], '', false); + $totalMock = $this->getMock( + '\Magento\Quote\Model\Quote\Address\Total', + ['getDiscountAmount', 'getDiscountDescription'], + [], + '', + false ); + + $totalMock->expects($this->once())->method('getDiscountAmount')->willReturn($discountAmount); + $totalMock->expects($this->once())->method('getDiscountDescription')->willReturn($discountDescription); + $this->assertEquals($expectedResult, $this->discount->fetch($quoteMock, $totalMock)); } } diff --git a/app/code/Magento/Tax/Block/Checkout/Grandtotal.php b/app/code/Magento/Tax/Block/Checkout/Grandtotal.php index 1d19a741ff484..bab3817bcf25e 100644 --- a/app/code/Magento/Tax/Block/Checkout/Grandtotal.php +++ b/app/code/Magento/Tax/Block/Checkout/Grandtotal.php @@ -52,7 +52,7 @@ public function __construct( */ public function includeTax() { - if ($this->getTotal()->getAddress()->getGrandTotal()) { + if ($this->getTotal()->getValue()) { return $this->_taxConfig->displayCartTaxWithGrandTotal($this->getStore()); } return false; @@ -65,7 +65,7 @@ public function includeTax() */ public function getTotalExclTax() { - $excl = $this->getTotal()->getAddress()->getGrandTotal() - $this->getTotal()->getAddress()->getTaxAmount(); + $excl = $this->getTotal()->getValue() - $this->_totals['tax']->getValue(); $excl = max($excl, 0); return $excl; } diff --git a/app/code/Magento/Tax/Block/Checkout/Shipping.php b/app/code/Magento/Tax/Block/Checkout/Shipping.php index 3b923cafe1be6..9d701b221efbb 100644 --- a/app/code/Magento/Tax/Block/Checkout/Shipping.php +++ b/app/code/Magento/Tax/Block/Checkout/Shipping.php @@ -72,7 +72,7 @@ public function displayIncludeTax() */ public function getShippingIncludeTax() { - return $this->getTotal()->getAddress()->getShippingInclTax(); + return $this->getTotal()->getShippingInclTax(); } /** @@ -82,7 +82,7 @@ public function getShippingIncludeTax() */ public function getShippingExcludeTax() { - return $this->getTotal()->getAddress()->getShippingAmount(); + return $this->getTotal()->getValue(); } /** @@ -94,7 +94,7 @@ public function getIncludeTaxLabel() { return __( 'Shipping Incl. Tax (%1)', - $this->escapeHtml($this->getTotal()->getAddress()->getShippingDescription()) + $this->escapeHtml($this->getQuote()->getShippingAddress()->getShippingDescription()) ); } @@ -107,7 +107,20 @@ public function getExcludeTaxLabel() { return __( 'Shipping Excl. Tax (%1)', - $this->escapeHtml($this->getTotal()->getAddress()->getShippingDescription()) + $this->escapeHtml($this->getQuote()->getShippingAddress()->getShippingDescription()) ); } + + /** + * Determine shipping visibility based on selected method. + * + * @return bool + */ + public function displayShipping() + { + if (!$this->getQuote()->getShippingAddress()->getShippingMethod()) { + return false; + } + return true; + } } diff --git a/app/code/Magento/Tax/Model/Quote/GrandTotalDetailsPlugin.php b/app/code/Magento/Tax/Model/Quote/GrandTotalDetailsPlugin.php index 1de7b6d223d56..9f52085e7d839 100644 --- a/app/code/Magento/Tax/Model/Quote/GrandTotalDetailsPlugin.php +++ b/app/code/Magento/Tax/Model/Quote/GrandTotalDetailsPlugin.php @@ -5,8 +5,7 @@ */ namespace Magento\Tax\Model\Quote; -use Magento\Quote\Model\Cart\CartTotalRepository; -use Magento\Quote\Api\Data\TotalsExtensionFactory; +use Magento\Quote\Api\Data\TotalSegmentExtensionFactory; class GrandTotalDetailsPlugin { @@ -21,14 +20,9 @@ class GrandTotalDetailsPlugin protected $ratesFactory; /** - * @var TotalsExtensionFactory + * @var TotalSegmentExtensionFactory */ - protected $extensionFactory; - - /** - * @var \Magento\Quote\Model\QuoteRepository - */ - protected $quoteRepository; + protected $totalSegmentExtensionFactory; /** * @var \Magento\Tax\Model\Config @@ -36,32 +30,27 @@ class GrandTotalDetailsPlugin protected $taxConfig; /** - * @var \Magento\Quote\Model\Quote\Address\Total\Tax + * @var string */ - protected $taxTotal; + protected $code; /** * @param \Magento\Tax\Api\Data\GrandTotalDetailsInterfaceFactory $detailsFactory * @param \Magento\Tax\Api\Data\GrandTotalRatesInterfaceFactory $ratesFactory - * @param TotalsExtensionFactory $extensionFactory + * @param TotalSegmentExtensionFactory $totalSegmentExtensionFactory * @param \Magento\Tax\Model\Config $taxConfig - * @param \Magento\Quote\Model\Quote\Address\Total\Tax $taxTotal - * @param \Magento\Quote\Model\QuoteRepository $quoteRepository */ public function __construct( \Magento\Tax\Api\Data\GrandTotalDetailsInterfaceFactory $detailsFactory, \Magento\Tax\Api\Data\GrandTotalRatesInterfaceFactory $ratesFactory, - TotalsExtensionFactory $extensionFactory, - \Magento\Tax\Model\Config $taxConfig, - \Magento\Quote\Model\Quote\Address\Total\Tax $taxTotal, - \Magento\Quote\Model\QuoteRepository $quoteRepository + TotalSegmentExtensionFactory $totalSegmentExtensionFactory, + \Magento\Tax\Model\Config $taxConfig ) { $this->detailsFactory = $detailsFactory; $this->ratesFactory = $ratesFactory; - $this->extensionFactory = $extensionFactory; + $this->totalSegmentExtensionFactory = $totalSegmentExtensionFactory; $this->taxConfig = $taxConfig; - $this->taxTotal = $taxTotal; - $this->quoteRepository = $quoteRepository; + $this->code = 'tax'; } /** @@ -81,31 +70,31 @@ protected function getRatesData($rates) } /** - * @param CartTotalRepository $subject + * @param \Magento\Quote\Model\Cart\TotalsConverter $subject * @param \Closure $proceed - * @param int $cartId - * @return \Magento\Quote\Model\Cart\Totals - * @throws \Magento\Framework\Exception\NoSuchEntityException + * @param \Magento\Quote\Model\Quote\Address\Total[] $addressTotals + * @return \Magento\Quote\Api\Data\TotalSegmentInterface[] * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function aroundGet(CartTotalRepository $subject, \Closure $proceed, $cartId) - { - $result = $proceed($cartId); - $quote = $this->quoteRepository->getActive($cartId); - $totals = $quote->getTotals(); + public function aroundProcess( + \Magento\Quote\Model\Cart\TotalsConverter $subject, + \Closure $proceed, + array $addressTotals = [] + ) { + $totalSegments = $proceed($addressTotals); - if (!array_key_exists('tax', $totals)) { - return $result; + if (!array_key_exists($this->code, $addressTotals)) { + return $totalSegments; } - $taxes = $totals['tax']->getData(); + $taxes = $addressTotals['tax']->getData(); if (!array_key_exists('full_info', $taxes)) { - return $result; + return $totalSegments; } $detailsId = 1; $finalData = []; - foreach ($taxes['full_info'] as $info) { + foreach (unserialize($taxes['full_info']) as $info) { if ((array_key_exists('hidden', $info) && $info['hidden']) || ($info['amount'] == 0 && $this->taxConfig->displayCartZeroTax()) ) { @@ -120,13 +109,12 @@ public function aroundGet(CartTotalRepository $subject, \Closure $proceed, $cart $finalData[] = $taxDetails; $detailsId++; } - $attributes = $result->getExtensionAttributes(); + $attributes = $totalSegments[$this->code]->getExtensionAttributes(); if ($attributes === null) { - $attributes = $this->extensionFactory->create(); + $attributes = $this->totalSegmentExtensionFactory->create(); } $attributes->setTaxGrandtotalDetails($finalData); - /** @var $result \Magento\Quote\Model\Cart\Totals */ - $result->setExtensionAttributes($attributes); - return $result; + $totalSegments[$this->code]->setExtensionAttributes($attributes); + return $totalSegments; } } diff --git a/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php b/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php index e82154f6ed71c..99ef5672ef536 100644 --- a/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php +++ b/app/code/Magento/Tax/Model/Sales/Total/Quote/CommonTaxCollector.php @@ -22,6 +22,7 @@ use Magento\Tax\Api\Data\TaxDetailsInterface; use Magento\Tax\Api\Data\TaxDetailsItemInterface; use Magento\Tax\Api\Data\QuoteDetailsInterface; +use Magento\Quote\Api\Data\ShippingAssignmentInterface; /** * Tax totals calculation model @@ -287,17 +288,17 @@ public function mapItemExtraTaxables( /** * Add quote items * - * @param QuoteAddress $address + * @param ShippingAssignmentInterface $shippingAssignment * @param bool $useBaseCurrency * @param bool $priceIncludesTax * @return \Magento\Tax\Api\Data\QuoteDetailsItemInterface[] */ public function mapItems( - QuoteAddress $address, + ShippingAssignmentInterface $shippingAssignment, $priceIncludesTax, $useBaseCurrency ) { - $items = $this->_getAddressItems($address); + $items = $shippingAssignment->getItems(); if (!count($items)) { return []; } @@ -361,43 +362,48 @@ public function populateAddressData(QuoteDetailsInterface $quoteDetails, QuoteAd } /** - * @param QuoteAddress $address + * @param ShippingAssignmentInterface $shippingAssignment + * @param QuoteAddress\Total $total * @param bool $useBaseCurrency * @return \Magento\Tax\Api\Data\QuoteDetailsItemInterface */ - public function getShippingDataObject(QuoteAddress $address, $useBaseCurrency) - { - if ($address->getShippingTaxCalculationAmount() === null) { + public function getShippingDataObject( + ShippingAssignmentInterface $shippingAssignment, + QuoteAddress\Total $total, + $useBaseCurrency + ) { + $store = $shippingAssignment->getShipping()->getAddress()->getQuote()->getStore(); + if ($total->getShippingTaxCalculationAmount() === null) { //Save the original shipping amount because shipping amount will be overridden //with shipping amount excluding tax - $address->setShippingTaxCalculationAmount($address->getShippingAmount()); - $address->setBaseShippingTaxCalculationAmount($address->getBaseShippingAmount()); + $total->setShippingTaxCalculationAmount($total->getShippingAmount()); + $total->setBaseShippingTaxCalculationAmount($total->getBaseShippingAmount()); } - if ($address->getShippingTaxCalculationAmount() !== null) { + if ($total->getShippingTaxCalculationAmount() !== null) { /** @var \Magento\Tax\Api\Data\QuoteDetailsItemInterface $itemDataObject */ $itemDataObject = $this->quoteDetailsItemDataObjectFactory->create() ->setType(self::ITEM_TYPE_SHIPPING) ->setCode(self::ITEM_CODE_SHIPPING) ->setQuantity(1); if ($useBaseCurrency) { - $itemDataObject->setUnitPrice($address->getBaseShippingTaxCalculationAmount()); + $itemDataObject->setUnitPrice($total->getBaseShippingTaxCalculationAmount()); } else { - $itemDataObject->setUnitPrice($address->getShippingTaxCalculationAmount()); + $itemDataObject->setUnitPrice($total->getShippingTaxCalculationAmount()); } - if ($address->getShippingDiscountAmount()) { + if ($total->getShippingDiscountAmount()) { if ($useBaseCurrency) { - $itemDataObject->setDiscountAmount($address->getBaseShippingDiscountAmount()); + $itemDataObject->setDiscountAmount($total->getBaseShippingDiscountAmount()); } else { - $itemDataObject->setDiscountAmount($address->getShippingDiscountAmount()); + $itemDataObject->setDiscountAmount($total->getShippingDiscountAmount()); } } $itemDataObject->setTaxClassKey( $this->taxClassKeyDataObjectFactory->create() ->setType(TaxClassKeyInterface::TYPE_ID) - ->setValue($this->_config->getShippingTaxClass($address->getQuote()->getStore())) + ->setValue($this->_config->getShippingTaxClass($store)) ); $itemDataObject->setIsTaxIncluded( - $this->_config->shippingPriceIncludesTax($address->getQuote()->getStore()) + $this->_config->shippingPriceIncludesTax($store) ); return $itemDataObject; } @@ -408,13 +414,14 @@ public function getShippingDataObject(QuoteAddress $address, $useBaseCurrency) /** * Populate QuoteDetails object from quote address object * - * @param QuoteAddress $address + * @param ShippingAssignmentInterface $shippingAssignment * @param \Magento\Tax\Api\Data\QuoteDetailsItemInterface[] $itemDataObjects * @return \Magento\Tax\Api\Data\QuoteDetailsInterface */ - protected function prepareQuoteDetails(QuoteAddress $address, $itemDataObjects) + protected function prepareQuoteDetails(ShippingAssignmentInterface $shippingAssignment, $itemDataObjects) { - $items = $this->_getAddressItems($address); + $items = $shippingAssignment->getItems(); + $address = $shippingAssignment->getShipping()->getAddress(); if (!count($items)) { return $this->quoteDetailsDataObjectFactory->create(); } @@ -471,15 +478,21 @@ protected function organizeItemTaxDetailsByType( * Set the following aggregated values in the quote object: * subtotal, subtotalInclTax, tax, discount_tax_compensation, * - * @param QuoteAddress $address + * @param ShippingAssignmentInterface $shippingAssignment * @param array $itemTaxDetails + * @param QuoteAddress\Total $total * @return $this */ - protected function processProductItems(QuoteAddress $address, array $itemTaxDetails) - { + protected function processProductItems( + ShippingAssignmentInterface $shippingAssignment, + array $itemTaxDetails, + QuoteAddress\Total $total + ) { + $store = $shippingAssignment->getShipping()->getAddress()->getQuote()->getStore(); + /** @var AbstractItem[] $keyedAddressItems */ $keyedAddressItems = []; - foreach ($this->_getAddressItems($address) as $addressItem) { + foreach ($shippingAssignment->getItems() as $addressItem) { $keyedAddressItems[$addressItem->getTaxCalculationItemId()] = $addressItem; } @@ -494,7 +507,7 @@ protected function processProductItems(QuoteAddress $address, array $itemTaxDeta /** @var TaxDetailsItemInterface $baseTaxDetail */ $baseTaxDetail = $itemTaxDetail[self::KEY_BASE_ITEM]; $quoteItem = $keyedAddressItems[$code]; - $this->updateItemTaxInfo($quoteItem, $taxDetail, $baseTaxDetail, $address->getQuote()->getStore()); + $this->updateItemTaxInfo($quoteItem, $taxDetail, $baseTaxDetail, $store); //Update aggregated values if ($quoteItem->getHasChildren() && $quoteItem->isChildrenCalculated()) { @@ -512,15 +525,16 @@ protected function processProductItems(QuoteAddress $address, array $itemTaxDeta } //Set aggregated values - $address->setTotalAmount('subtotal', $subtotal); - $address->setBaseTotalAmount('subtotal', $baseSubtotal); - $address->setTotalAmount('tax', $tax); - $address->setBaseTotalAmount('tax', $baseTax); - $address->setTotalAmount('discount_tax_compensation', $discountTaxCompensation); - $address->setBaseTotalAmount('discount_tax_compensation', $baseDiscountTaxCompensation); + $total->setTotalAmount('subtotal', $subtotal); + $total->setBaseTotalAmount('subtotal', $baseSubtotal); + $total->setTotalAmount('tax', $tax); + $total->setBaseTotalAmount('tax', $baseTax); + $total->setTotalAmount('discount_tax_compensation', $discountTaxCompensation); + $total->setBaseTotalAmount('discount_tax_compensation', $baseDiscountTaxCompensation); - $address->setSubtotalInclTax($subtotalInclTax); - $address->setBaseSubtotalInclTax($baseSubtotalInclTax); + $total->setSubtotalInclTax($subtotalInclTax); + $total->setBaseSubtotalTotalInclTax($baseSubtotalInclTax); + $total->setBaseSubtotalInclTax($baseSubtotalInclTax); return $this; } @@ -528,18 +542,21 @@ protected function processProductItems(QuoteAddress $address, array $itemTaxDeta /** * Process applied taxes for items and quote * - * @param QuoteAddress $address + * @param QuoteAddress\Total $total * @param array $itemsByType * @return $this */ - protected function processAppliedTaxes(QuoteAddress $address, Array $itemsByType) - { - $address->setAppliedTaxes([]); + protected function processAppliedTaxes( + QuoteAddress\Total $total, + ShippingAssignmentInterface $shippingAssignment, + Array $itemsByType + ) { + $total->setAppliedTaxes([]); $allAppliedTaxesArray = []; /** @var AbstractItem[] $keyedAddressItems */ $keyedAddressItems = []; - foreach ($this->_getAddressItems($address) as $addressItem) { + foreach ($shippingAssignment->getItems() as $addressItem) { $keyedAddressItems[$addressItem->getTaxCalculationItemId()] = $addressItem; } @@ -585,7 +602,7 @@ protected function processAppliedTaxes(QuoteAddress $address, Array $itemsByType foreach ($appliedTaxesArray as $appliedTaxArray) { $this->_saveAppliedTaxes( - $address, + $total, [$appliedTaxArray], $appliedTaxArray['amount'], $appliedTaxArray['base_amount'], @@ -595,7 +612,7 @@ protected function processAppliedTaxes(QuoteAddress $address, Array $itemsByType } } - $address->setItemsAppliedTaxes($allAppliedTaxesArray); + $total->setItemsAppliedTaxes($allAppliedTaxesArray); return $this; } @@ -644,30 +661,35 @@ public function updateItemTaxInfo($quoteItem, $itemTaxDetails, $baseItemTaxDetai /** * Update tax related fields for shipping * - * @param QuoteAddress $address + * @param ShippingAssignmentInterface $shippingAssignment + * @param QuoteAddress\Total $total * @param TaxDetailsItemInterface $shippingTaxDetails * @param TaxDetailsItemInterface $baseShippingTaxDetails * @return $this */ - protected function processShippingTaxInfo(QuoteAddress $address, $shippingTaxDetails, $baseShippingTaxDetails) - { - $address->setTotalAmount('shipping', $shippingTaxDetails->getRowTotal()); - $address->setBaseTotalAmount('shipping', $baseShippingTaxDetails->getRowTotal()); - $address->setTotalAmount('shipping_discount_tax_compensation', $shippingTaxDetails->getDiscountTaxCompensationAmount()); - $address->setBaseTotalAmount('shipping_discount_tax_compensation', $baseShippingTaxDetails->getDiscountTaxCompensationAmount()); + protected function processShippingTaxInfo( + ShippingAssignmentInterface $shippingAssignment, + QuoteAddress\Total $total, + $shippingTaxDetails, + $baseShippingTaxDetails + ) { + $total->setTotalAmount('shipping', $shippingTaxDetails->getRowTotal()); + $total->setBaseTotalAmount('shipping', $baseShippingTaxDetails->getRowTotal()); + $total->setTotalAmount('shipping_discount_tax_compensation', $shippingTaxDetails->getDiscountTaxCompensationAmount()); + $total->setBaseTotalAmount('shipping_discount_tax_compensation', $baseShippingTaxDetails->getDiscountTaxCompensationAmount()); - $address->setShippingInclTax($shippingTaxDetails->getRowTotalInclTax()); - $address->setBaseShippingInclTax($baseShippingTaxDetails->getRowTotalInclTax()); - $address->setShippingTaxAmount($shippingTaxDetails->getRowTax()); - $address->setBaseShippingTaxAmount($baseShippingTaxDetails->getRowTax()); + $total->setShippingInclTax($shippingTaxDetails->getRowTotalInclTax()); + $total->setBaseShippingInclTax($baseShippingTaxDetails->getRowTotalInclTax()); + $total->setShippingTaxAmount($shippingTaxDetails->getRowTax()); + $total->setBaseShippingTaxAmount($baseShippingTaxDetails->getRowTax()); //Add the shipping tax to total tax amount - $address->addTotalAmount('tax', $shippingTaxDetails->getRowTax()); - $address->addBaseTotalAmount('tax', $baseShippingTaxDetails->getRowTax()); + $total->addTotalAmount('tax', $shippingTaxDetails->getRowTax()); + $total->addBaseTotalAmount('tax', $baseShippingTaxDetails->getRowTax()); - if ($this->_config->discountTax($address->getQuote()->getStore())) { - $address->setShippingAmountForDiscount($shippingTaxDetails->getRowTotalInclTax()); - $address->setBaseShippingAmountForDiscount($baseShippingTaxDetails->getRowTotalInclTax()); + if ($this->_config->discountTax($shippingAssignment->getShipping()->getAddress()->getQuote()->getStore())) { + $total->setShippingAmountForDiscount($shippingTaxDetails->getRowTotalInclTax()); + $total->setBaseShippingAmountForDiscount($baseShippingTaxDetails->getRowTotalInclTax()); } return $this; @@ -722,7 +744,7 @@ public function convertAppliedTaxes($appliedTaxes, $baseAppliedTaxes, $extraInfo /** * Collect applied tax rates information on address level * - * @param QuoteAddress $address + * @param QuoteAddress\Total $total * @param array $applied * @param float $amount * @param float $baseAmount @@ -732,13 +754,13 @@ public function convertAppliedTaxes($appliedTaxes, $baseAppliedTaxes, $extraInfo * @SuppressWarnings(PHPMD.NPathComplexity) */ protected function _saveAppliedTaxes( - QuoteAddress $address, + QuoteAddress\Total $total, $applied, $amount, $baseAmount, $rate ) { - $previouslyAppliedTaxes = $address->getAppliedTaxes(); + $previouslyAppliedTaxes = $total->getAppliedTaxes(); $process = count($previouslyAppliedTaxes); foreach ($applied as $row) { @@ -774,7 +796,7 @@ protected function _saveAppliedTaxes( unset($previouslyAppliedTaxes[$row['id']]); } } - $address->setAppliedTaxes($previouslyAppliedTaxes); + $total->setAppliedTaxes($previouslyAppliedTaxes); } /** diff --git a/app/code/Magento/Tax/Model/Sales/Total/Quote/Shipping.php b/app/code/Magento/Tax/Model/Sales/Total/Quote/Shipping.php index 239baa4dabf27..3b76f9e20c8ef 100644 --- a/app/code/Magento/Tax/Model/Sales/Total/Quote/Shipping.php +++ b/app/code/Magento/Tax/Model/Sales/Total/Quote/Shipping.php @@ -6,44 +6,67 @@ namespace Magento\Tax\Model\Sales\Total\Quote; use Magento\Quote\Model\Quote\Address; +use Magento\Quote\Api\Data\ShippingAssignmentInterface; class Shipping extends CommonTaxCollector { /** * Collect tax totals for shipping. The result can be used to calculate discount on shipping * - * @param Address $address - * @return $this + * @param \Magento\Quote\Model\Quote $quote + * @param ShippingAssignmentInterface $shippingAssignment + * @param Address\Total $total + * @return $this */ - public function collect(Address $address) - { - parent::collect($address); - $items = $this->_getAddressItems($address); + public function collect( + \Magento\Quote\Model\Quote $quote, + \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment, + \Magento\Quote\Model\Quote\Address\Total $total + ) { + $storeId = $quote->getStoreId(); + $items = $shippingAssignment->getItems(); if (!$items) { return $this; } //Add shipping - $shippingDataObject = $this->getShippingDataObject($address, false); - $baseShippingDataObject = $this->getShippingDataObject($address, true); + $shippingDataObject = $this->getShippingDataObject($shippingAssignment, $total, false); + $baseShippingDataObject = $this->getShippingDataObject($shippingAssignment, $total, true); if ($shippingDataObject == null || $baseShippingDataObject == null) { return $this; } - $quoteDetails = $this->prepareQuoteDetails($address, [$shippingDataObject]); + $quoteDetails = $this->prepareQuoteDetails($shippingAssignment, [$shippingDataObject]); $taxDetails = $this->taxCalculationService - ->calculateTax($quoteDetails, $address->getQuote()->getStore()->getStoreId()); + ->calculateTax($quoteDetails, $storeId); - $baseQuoteDetails = $this->prepareQuoteDetails($address, [$baseShippingDataObject]); + $baseQuoteDetails = $this->prepareQuoteDetails($shippingAssignment, [$baseShippingDataObject]); $baseTaxDetails = $this->taxCalculationService - ->calculateTax($baseQuoteDetails, $address->getQuote()->getStore()->getStoreId()); + ->calculateTax($baseQuoteDetails, $storeId); $this->processShippingTaxInfo( - $address, + $shippingAssignment, + $total, $taxDetails->getItems()[self::ITEM_CODE_SHIPPING], $baseTaxDetails->getItems()[self::ITEM_CODE_SHIPPING] ); return $this; } + + /** + * @param \Magento\Quote\Model\Quote $quote + * @param Address\Total $total + * @return array|null + */ + public function fetch(\Magento\Quote\Model\Quote $quote, \Magento\Quote\Model\Quote\Address\Total $total) + { + if ($total->getShippingInclTax()) { + return [ + 'code' => 'shipping', + 'shipping_incl_tax' => $total->getShippingInclTax() + ]; + } + return null; + } } diff --git a/app/code/Magento/Tax/Model/Sales/Total/Quote/Subtotal.php b/app/code/Magento/Tax/Model/Sales/Total/Quote/Subtotal.php index fa69a30c6bada..9fc7f77046c67 100644 --- a/app/code/Magento/Tax/Model/Sales/Total/Quote/Subtotal.php +++ b/app/code/Magento/Tax/Model/Sales/Total/Quote/Subtotal.php @@ -10,6 +10,7 @@ namespace Magento\Tax\Model\Sales\Total\Quote; use Magento\Quote\Model\Quote\Address; +use Magento\Quote\Api\Data\ShippingAssignmentInterface; class Subtotal extends CommonTaxCollector { @@ -17,36 +18,52 @@ class Subtotal extends CommonTaxCollector * Calculate tax on product items. The result will be used to determine shipping * and discount later. * - * @param Address $address - * @return $this + * @param \Magento\Quote\Model\Quote $quote + * @param ShippingAssignmentInterface $shippingAssignment + * @param Address\Total $total + * @return $this */ - public function collect(Address $address) - { - parent::collect($address); - $items = $this->_getAddressItems($address); + public function collect( + \Magento\Quote\Model\Quote $quote, + \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment, + \Magento\Quote\Model\Quote\Address\Total $total + ) { + $items = $shippingAssignment->getItems(); if (!$items) { return $this; } - $priceIncludesTax = $this->_config->priceIncludesTax($address->getQuote()->getStore()); + $store = $quote->getStore(); + $priceIncludesTax = $this->_config->priceIncludesTax($store); //Setup taxable items - $itemDataObjects = $this->mapItems($address, $priceIncludesTax, false); - $quoteDetails = $this->prepareQuoteDetails($address, $itemDataObjects); + $itemDataObjects = $this->mapItems($shippingAssignment, $priceIncludesTax, false); + $quoteDetails = $this->prepareQuoteDetails($shippingAssignment, $itemDataObjects); $taxDetails = $this->taxCalculationService - ->calculateTax($quoteDetails, $address->getQuote()->getStore()->getStoreId()); + ->calculateTax($quoteDetails, $store->getStoreId()); - $itemDataObjects = $this->mapItems($address, $priceIncludesTax, true); - $baseQuoteDetails = $this->prepareQuoteDetails($address, $itemDataObjects); + $itemDataObjects = $this->mapItems($shippingAssignment, $priceIncludesTax, true); + $baseQuoteDetails = $this->prepareQuoteDetails($shippingAssignment, $itemDataObjects); $baseTaxDetails = $this->taxCalculationService - ->calculateTax($baseQuoteDetails, $address->getQuote()->getStore()->getStoreId()); + ->calculateTax($baseQuoteDetails, $store->getStoreId()); $itemsByType = $this->organizeItemTaxDetailsByType($taxDetails, $baseTaxDetails); if (isset($itemsByType[self::ITEM_TYPE_PRODUCT])) { - $this->processProductItems($address, $itemsByType[self::ITEM_TYPE_PRODUCT]); + $this->processProductItems($shippingAssignment, $itemsByType[self::ITEM_TYPE_PRODUCT], $total); } return $this; } + + /** + * @param \Magento\Quote\Model\Quote $quote + * @param Address\Total $total + * @return null + * @codeCoverageIgnore + */ + public function fetch(\Magento\Quote\Model\Quote $quote, \Magento\Quote\Model\Quote\Address\Total $total) + { + return null; + } } diff --git a/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php b/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php index 00ba67b0d113b..c1cd2b9927c2f 100644 --- a/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php +++ b/app/code/Magento/Tax/Model/Sales/Total/Quote/Tax.php @@ -10,6 +10,7 @@ use Magento\Quote\Model\Quote\Address; use Magento\Tax\Api\Data\TaxClassKeyInterface; use Magento\Tax\Model\Calculation; +use Magento\Quote\Api\Data\ShippingAssignmentInterface; /** * Tax totals calculation model @@ -83,43 +84,46 @@ public function __construct( /** * Collect tax totals for quote address * - * @param Address $address - * @return $this + * @param \Magento\Quote\Model\Quote $quote + * @param ShippingAssignmentInterface $shippingAssignment + * @param Address\Total $total + * @return $this */ - public function collect(Address $address) - { - parent::collect($address); - $this->clearValues($address); - $items = $this->_getAddressItems($address); - if (!$items) { + public function collect( + \Magento\Quote\Model\Quote $quote, + \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment, + \Magento\Quote\Model\Quote\Address\Total $total + ) { + $this->clearValues($total); + if (!$shippingAssignment->getItems()) { return $this; } - $baseTaxDetails = $this->getQuoteTaxDetails($address, true); - $taxDetails = $this->getQuoteTaxDetails($address, false); + $baseTaxDetails = $this->getQuoteTaxDetails($shippingAssignment, $total, true); + $taxDetails = $this->getQuoteTaxDetails($shippingAssignment, $total, false); //Populate address and items with tax calculation results $itemsByType = $this->organizeItemTaxDetailsByType($taxDetails, $baseTaxDetails); if (isset($itemsByType[self::ITEM_TYPE_PRODUCT])) { - $this->processProductItems($address, $itemsByType[self::ITEM_TYPE_PRODUCT]); + $this->processProductItems($shippingAssignment, $itemsByType[self::ITEM_TYPE_PRODUCT], $total); } if (isset($itemsByType[self::ITEM_TYPE_SHIPPING])) { $shippingTaxDetails = $itemsByType[self::ITEM_TYPE_SHIPPING][self::ITEM_CODE_SHIPPING][self::KEY_ITEM]; $baseShippingTaxDetails = $itemsByType[self::ITEM_TYPE_SHIPPING][self::ITEM_CODE_SHIPPING][self::KEY_BASE_ITEM]; - $this->processShippingTaxInfo($address, $shippingTaxDetails, $baseShippingTaxDetails); + $this->processShippingTaxInfo($shippingAssignment, $total, $shippingTaxDetails, $baseShippingTaxDetails); } //Process taxable items that are not product or shipping - $this->processExtraTaxables($address, $itemsByType); + $this->processExtraTaxables($total, $itemsByType); //Save applied taxes for each item and the quote in aggregation - $this->processAppliedTaxes($address, $itemsByType); + $this->processAppliedTaxes($total, $shippingAssignment, $itemsByType); if ($this->includeExtraTax()) { - $this->_addAmount($address->getExtraTaxAmount()); - $this->_addBaseAmount($address->getBaseExtraTaxAmount()); + $total->addTotalAmount('extra_tax', $total->getExtraTaxAmount()); + $total->addBaseTotalAmount('extra_tax', $total->getBaseExtraTaxAmount()); } return $this; @@ -128,38 +132,40 @@ public function collect(Address $address) /** * Clear tax related total values in address * - * @param Address $address + * @param Address\Total $total * @return void */ - protected function clearValues(Address $address) + protected function clearValues(Address\Total $total) { - $address->setTotalAmount('subtotal', 0); - $address->setBaseTotalAmount('subtotal', 0); - $address->setTotalAmount('tax', 0); - $address->setBaseTotalAmount('tax', 0); - $address->setTotalAmount('discount_tax_compensation', 0); - $address->setBaseTotalAmount('discount_tax_compensation', 0); - $address->setTotalAmount('shipping_discount_tax_compensation', 0); - $address->setBaseTotalAmount('shipping_discount_tax_compensation', 0); - $address->setSubtotalInclTax(0); - $address->setBaseSubtotalInclTax(0); + $total->setTotalAmount('subtotal', 0); + $total->setBaseTotalAmount('subtotal', 0); + $total->setTotalAmount('tax', 0); + $total->setBaseTotalAmount('tax', 0); + $total->setTotalAmount('discount_tax_compensation', 0); + $total->setBaseTotalAmount('discount_tax_compensation', 0); + $total->setTotalAmount('shipping_discount_tax_compensation', 0); + $total->setBaseTotalAmount('shipping_discount_tax_compensation', 0); + $total->setSubtotalInclTax(0); + $total->setBaseSubtotalInclTax(0); } /** * Call tax calculation service to get tax details on the quote and items * - * @param Address $address + * @param ShippingAssignmentInterface $shippingAssignment + * @param Address\Total $total * @param bool $useBaseCurrency * @return \Magento\Tax\Api\Data\TaxDetailsInterface */ - protected function getQuoteTaxDetails($address, $useBaseCurrency) + protected function getQuoteTaxDetails($shippingAssignment, $total, $useBaseCurrency) { + $address = $shippingAssignment->getShipping()->getAddress(); //Setup taxable items $priceIncludesTax = $this->_config->priceIncludesTax($address->getQuote()->getStore()); - $itemDataObjects = $this->mapItems($address, $priceIncludesTax, $useBaseCurrency); + $itemDataObjects = $this->mapItems($shippingAssignment, $priceIncludesTax, $useBaseCurrency); //Add shipping - $shippingDataObject = $this->getShippingDataObject($address, $useBaseCurrency); + $shippingDataObject = $this->getShippingDataObject($shippingAssignment, $total, $useBaseCurrency); if ($shippingDataObject != null) { $itemDataObjects[] = $shippingDataObject; } @@ -175,7 +181,7 @@ protected function getQuoteTaxDetails($address, $useBaseCurrency) } //Preparation for calling taxCalculationService - $quoteDetails = $this->prepareQuoteDetails($address, $itemDataObjects); + $quoteDetails = $this->prepareQuoteDetails($shippingAssignment, $itemDataObjects); $taxDetails = $this->taxCalculationService ->calculateTax($quoteDetails, $address->getQuote()->getStore()->getStoreId()); @@ -228,12 +234,12 @@ public function mapQuoteExtraTaxables( /** * Process everything other than product or shipping, save the result in quote * - * @param Address $address + * @param Address\Total $total * @param array $itemsByType * @return $this * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ - protected function processExtraTaxables(Address $address, Array $itemsByType) + protected function processExtraTaxables(Address\Total $total, Array $itemsByType) { $extraTaxableDetails = []; foreach ($itemsByType as $itemType => $itemTaxDetails) { @@ -267,77 +273,71 @@ protected function processExtraTaxables(Address $address, Array $itemsByType) self::KEY_TAX_DETAILS_APPLIED_TAXES => $appliedTaxesArray, ]; - $address->addTotalAmount('tax', $taxDetails->getRowTax()); - $address->addBaseTotalAmount('tax', $baseTaxDetails->getRowTax()); + $total->addTotalAmount('tax', $taxDetails->getRowTax()); + $total->addBaseTotalAmount('tax', $baseTaxDetails->getRowTax()); //TODO: save applied taxes for the item } } } - $address->setExtraTaxableDetails($extraTaxableDetails); + $total->setExtraTaxableDetails($extraTaxableDetails); return $this; } /** * Add tax totals information to address object * - * @param Address $address - * @return $this + * @param \Magento\Quote\Model\Quote $quote + * @param Address\Total $total + * @return array|null * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ - public function fetch(Address $address) + public function fetch(\Magento\Quote\Model\Quote $quote, \Magento\Quote\Model\Quote\Address\Total $total) { - $applied = $address->getAppliedTaxes(); - $store = $address->getQuote()->getStore(); - $amount = $address->getTaxAmount(); - - $items = $this->_getAddressItems($address); - $discountTaxCompensation = 0; - foreach ($items as $item) { - $discountTaxCompensation += $item->getDiscountTaxCompensation(); - } - $taxAmount = $amount + $discountTaxCompensation; + $totals = []; + $store = $quote->getStore(); + $applied = $total->getAppliedTaxes(); + $amount = $total->getTaxAmount(); + $taxAmount = $amount + $total->getTotalAmount('discount_tax_compensation'); $area = null; - if ($this->_config->displayCartTaxWithGrandTotal($store) && $address->getGrandTotal()) { + if ($this->_config->displayCartTaxWithGrandTotal($store) && $total->getGrandTotal()) { $area = 'taxes'; } if ($amount != 0 || $this->_config->displayCartZeroTax($store)) { - $address->addTotal( - [ - 'code' => $this->getCode(), - 'title' => __('Tax'), - 'full_info' => $applied ? $applied : [], - 'value' => $amount, - 'area' => $area, - ] - ); + $totals[] = [ + 'code' => $this->getCode(), + 'title' => __('Tax'), + 'full_info' => $applied ? $applied : [], + 'value' => $amount, + 'area' => $area, + ]; } - $store = $address->getQuote()->getStore(); /** * Modify subtotal */ if ($this->_config->displayCartSubtotalBoth($store) || $this->_config->displayCartSubtotalInclTax($store)) { - if ($address->getSubtotalInclTax() > 0) { - $subtotalInclTax = $address->getSubtotalInclTax(); + if ($total->getSubtotalInclTax() > 0) { + $subtotalInclTax = $total->getSubtotalInclTax(); } else { - $subtotalInclTax = $address->getSubtotal() + $taxAmount - $address->getShippingTaxAmount(); + $subtotalInclTax = $total->getSubtotal() + $taxAmount - $total->getShippingTaxAmount(); } - $address->addTotal( - [ - 'code' => 'subtotal', - 'title' => __('Subtotal'), - 'value' => $subtotalInclTax, - 'value_incl_tax' => $subtotalInclTax, - 'value_excl_tax' => $address->getSubtotal(), - ] - ); + $totals[] = [ + 'code' => 'subtotal', + 'title' => __('Subtotal'), + 'value' => $subtotalInclTax, + 'value_incl_tax' => $subtotalInclTax, + 'value_excl_tax' => $total->getSubtotal(), + ]; } - return $this; + if (empty($totals)) { + return null; + } + return $totals; } /** diff --git a/app/code/Magento/Tax/Observer/QuoteCollectTotalsBefore.php b/app/code/Magento/Tax/Observer/QuoteCollectTotalsBefore.php deleted file mode 100644 index 5161aed3be38f..0000000000000 --- a/app/code/Magento/Tax/Observer/QuoteCollectTotalsBefore.php +++ /dev/null @@ -1,29 +0,0 @@ -getEvent()->getQuote(); - foreach ($quote->getAllAddresses() as $address) { - $address->setExtraTaxAmount(0); - $address->setBaseExtraTaxAmount(0); - } - return $this; - } -} diff --git a/app/code/Magento/Tax/Test/Unit/Block/Checkout/ShippingTest.php b/app/code/Magento/Tax/Test/Unit/Block/Checkout/ShippingTest.php new file mode 100644 index 0000000000000..a292087aeb27f --- /dev/null +++ b/app/code/Magento/Tax/Test/Unit/Block/Checkout/ShippingTest.php @@ -0,0 +1,54 @@ +quoteMock = $this->getMock('Magento\Quote\Model\Quote', [], [], '', false); + $checkoutSession = $this->getMock('\Magento\Checkout\Model\Session', [], [], '', false); + $checkoutSession->expects($this->any())->method('getQuote')->willReturn($this->quoteMock); + + $this->model = $objectManager->getObject( + 'Magento\Tax\Block\Checkout\Shipping', + ['checkoutSession' => $checkoutSession] + ); + } + + /** + * @param string $shippingMethod + * @param bool $expectedResult + * @dataProvider displayShippingDataProvider + */ + public function testDisplayShipping($shippingMethod, $expectedResult) + { + $addressMock = $this->getMock('Magento\Quote\Model\Quote\Address', ['getShippingMethod'], [], '', false); + $this->quoteMock->expects($this->once())->method('getShippingAddress')->willReturn($addressMock); + $addressMock->expects($this->once())->method('getShippingMethod')->willReturn($shippingMethod); + + $this->assertEquals($expectedResult, $this->model->displayShipping()); + } + + public function displayShippingDataProvider() + { + return [ + ["flatrate_flatrate", true], + [null, false] + ]; + } +} diff --git a/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/CommonTaxCollectorTest.php b/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/CommonTaxCollectorTest.php index 355417a7feab5..dc9ff360ad7e6 100644 --- a/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/CommonTaxCollectorTest.php +++ b/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/CommonTaxCollectorTest.php @@ -85,7 +85,6 @@ public function setUp() $this->address = $this->getMockBuilder('\Magento\Quote\Model\Quote\Address') ->disableOriginalConstructor() - ->setMethods(['__wakeup', 'getQuote', 'getShippingDiscountAmount', 'getBaseShippingDiscountAmount']) ->getMock(); $this->address->expects($this->any()) @@ -127,8 +126,23 @@ public function testGetShippingDataObject( $shippingTaxClass, $shippingPriceInclTax ) { + $shippingAssignmentMock = $this->getMock('Magento\Quote\Api\Data\ShippingAssignmentInterface'); + $methods = [ + 'getShippingDiscountAmount', + 'getShippingTaxCalculationAmount', + 'setShippingTaxCalculationAmount', + 'getShippingAmount', + 'setBaseShippingTaxCalculationAmount', + 'getBaseShippingAmount', + 'getBaseShippingDiscountAmount' + ]; + $totalsMock = $this->getMock('Magento\Quote\Model\Quote\Address\Total', $methods, [], '', false); + $shippingMock = $this->getMock('Magento\Quote\Api\Data\ShippingInterface'); + $shippingAssignmentMock->expects($this->once())->method('getShipping')->willReturn($shippingMock); + $shippingMock->expects($this->once())->method('getAddress')->willReturn($this->address); $baseShippingAmount = $addressData['base_shipping_amount']; $shippingAmount = $addressData['shipping_amount']; + $totalsMock->expects($this->any())->method('getShippingTaxCalculationAmount')->willReturn($shippingAmount); $this->taxConfig->expects($this->any()) ->method('getShippingTaxClass') ->with($this->store) @@ -137,27 +151,28 @@ public function testGetShippingDataObject( ->method('shippingPriceIncludesTax') ->with($this->store) ->will($this->returnValue($shippingPriceInclTax)); - $this->address + $totalsMock ->expects($this->atLeastOnce()) ->method('getShippingDiscountAmount') ->willReturn($shippingAmount); if ($shippingAmount) { if ($useBaseCurrency && $shippingAmount != 0) { - $this->address + $totalsMock ->expects($this->once()) ->method('getBaseShippingDiscountAmount') ->willReturn($baseShippingAmount); $expectedDiscountAmount = $baseShippingAmount; } else { - $this->address->expects($this->never())->method('getBaseShippingDiscountAmount'); + $totalsMock->expects($this->never())->method('getBaseShippingDiscountAmount'); $expectedDiscountAmount = $shippingAmount; } } foreach ($addressData as $key => $value) { - $this->address->setData($key, $value); + $totalsMock->setData($key, $value); } $this->assertEquals($this->quoteDetailsItemDataObject, - $this->commonTaxCollector->getShippingDataObject($this->address, $useBaseCurrency)); + $this->commonTaxCollector->getShippingDataObject($shippingAssignmentMock, $totalsMock, $useBaseCurrency)); + if ($shippingAmount) { $this->assertEquals($expectedDiscountAmount, $this->quoteDetailsItemDataObject->getDiscountAmount()); } diff --git a/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/ShippingTest.php b/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/ShippingTest.php index 7408408ae5c5f..88cdf3e41b466 100644 --- a/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/ShippingTest.php +++ b/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/ShippingTest.php @@ -47,6 +47,11 @@ class ShippingTest extends \PHPUnit_Framework_TestCase */ private $regionFactoryMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $quoteMock; + /** * @var Shipping */ @@ -88,6 +93,7 @@ protected function setUp() '', false ); + $this->quoteMock = $this->getMock('\Magento\Quote\Model\Quote', [], [], '', false); $this->model = new Shipping( $this->taxConfigMock, $this->taxCalculationMock, @@ -102,25 +108,29 @@ protected function setUp() public function testCollectDoesNotCalculateTaxIfThereIsNoItemsRelatedToGivenAddress() { $storeId = 1; - $storeMock = $this->getMockObject('Magento\Store\Model\Store', [ - 'store_id' => $storeId, - ]); - $quoteMock = $this->getMockObject( - 'Magento\Quote\Model\Quote', - [ - 'store' => $storeMock, - ] - ); + $this->quoteMock->expects($this->once())->method('getStoreId')->willReturn($storeId); + $addressMock = $this->getMockObject('Magento\Quote\Model\Quote\Address', [ 'all_items' => [], 'shipping_tax_calculation_amount' => 100, 'base_shipping_tax_calculation_amount' => 200, 'shipping_discount_amount' => 10, 'base_shipping_discount_amount' => 20, - 'quote' => $quoteMock, + 'quote' => $this->quoteMock, ]); $this->taxCalculationMock->expects($this->never())->method('calculateTax'); - $this->model->collect($addressMock); + + $shippingMock = $this->getMock('\Magento\Quote\Api\Data\ShippingInterface'); + $shippingMock->expects($this->atLeastOnce())->method('getAddress')->willReturn($addressMock); + $shippingAssignmentMock = $this->getMock('\Magento\Quote\Api\Data\ShippingAssignmentInterface'); + $shippingAssignmentMock->expects($this->atLeastOnce())->method('getShipping')->willReturn($shippingMock); + $shippingAssignmentMock->expects($this->once()) + ->method('getItems') + ->willReturn([$this->getMock('\Magento\Quote\Api\Data\CartItemInterface')]); + + $totalMock = $this->getMock('\Magento\Quote\Model\Quote\Address\Total', [], [], '', false); + + $this->model->collect($this->quoteMock, $shippingAssignmentMock, $totalMock); } public function testCollect() @@ -152,4 +162,26 @@ private function getMockObject($className, array $objectState) return $mock; } + + public function testFetch() + { + $value = 42; + $total = new \Magento\Quote\Model\Quote\Address\Total(); + $total->setShippingInclTax($value); + $expectedResult = [ + 'code' => 'shipping', + 'shipping_incl_tax' => $value + ]; + + $this->assertEquals($expectedResult, $this->model->fetch($this->quoteMock, $total)); + } + + public function testFetchWithZeroShipping() + { + $value = 0; + $total = new \Magento\Quote\Model\Quote\Address\Total(); + $total->setShippingInclTax($value); + + $this->assertNull($this->model->fetch($this->quoteMock, $total)); + } } diff --git a/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/SubtotalTest.php b/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/SubtotalTest.php index 45ea692bfc887..53000c346c03a 100644 --- a/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/SubtotalTest.php +++ b/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/SubtotalTest.php @@ -52,6 +52,21 @@ class SubtotalTest extends \PHPUnit_Framework_TestCase */ protected $storeMock; + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $shippingAssignmentMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $totalsMock; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + protected $shippingMock; + /** * @var \Magento\Tax\Model\Sales\Total\Quote\Subtotal */ @@ -60,7 +75,9 @@ class SubtotalTest extends \PHPUnit_Framework_TestCase protected function setUp() { $this->objectManager = new ObjectManager($this); - + $this->totalsMock = $this->getMock('Magento\Quote\Model\Quote\Address\Total', [], [], '', false); + $this->shippingAssignmentMock = $this->getMock('Magento\Quote\Api\Data\ShippingAssignmentInterface'); + $this->shippingMock = $this->getMock('Magento\Quote\Api\Data\ShippingInterface'); $this->taxConfigMock = $this->getMockBuilder('\Magento\Tax\Model\Config') ->disableOriginalConstructor() ->setMethods(['priceIncludesTax', 'getShippingTaxClass', 'shippingPriceIncludesTax', 'discountTax']) @@ -70,7 +87,7 @@ protected function setUp() $this->quoteDetailsDataObjectFactoryMock = $this->getMockBuilder('\Magento\Tax\Api\Data\QuoteDetailsInterfaceFactory') ->disableOriginalConstructor() - ->setMethods(['create'])->getMock(); + ->setMethods(['create', 'setBillingAddress', 'setShippingAddress'])->getMock(); $this->keyDataObjectFactoryMock = $this->getMock( 'Magento\Tax\Api\Data\TaxClassKeyInterfaceFactory', ['create'], @@ -135,7 +152,7 @@ protected function setUp() $this->quoteMock = $this->getMockBuilder('\Magento\Quote\Model\Quote') ->disableOriginalConstructor() ->getMock(); - + $this->addressMock->expects($this->any())->method('getQuote')->willReturn($this->quoteMock); $this->storeMock = $this->getMockBuilder('\Magento\Store\Model\Store')->disableOriginalConstructor()->getMock(); $this->quoteMock->expects($this->any())->method('getStore')->willReturn($this->storeMock); $this->storeMock->expects($this->any())->method('getStoreId')->willReturn(111); @@ -143,9 +160,9 @@ protected function setUp() public function testCollectEmptyAddresses() { - $this->addressMock->expects($this->once())->method('getAllItems')->willReturn(null); + $this->shippingAssignmentMock->expects($this->once())->method('getItems')->willReturn(null); $this->taxConfigMock->expects($this->never())->method('priceIncludesTax'); - $this->model->collect($this->addressMock); + $this->model->collect($this->quoteMock, $this->shippingAssignmentMock, $this->totalsMock); } public function testCollect() @@ -160,7 +177,7 @@ public function testCollect() ->getMockForAbstractClass(); $this->taxCalculationMock->expects($this->atLeastOnce())->method('calculateTax')->willReturn($taxDetailsMock); $taxDetailsMock->expects($this->atLeastOnce())->method('getItems')->willReturn([]); - $this->model->collect($this->addressMock); + $this->model->collect($this->quoteMock, $this->shippingAssignmentMock, $this->totalsMock); } /** @@ -169,15 +186,15 @@ public function testCollect() protected function checkGetAddressItems() { $customerTaxClassId = 2425; - $this->addressMock->expects($this->atLeastOnce()) - ->method('getAllItems')->willReturn([$this->addressMock]); + $this->shippingAssignmentMock->expects($this->atLeastOnce()) + ->method('getItems')->willReturn([$this->addressMock]); + $this->shippingAssignmentMock->expects($this->any())->method('getShipping')->willReturn($this->shippingMock); - $this->addressMock->expects($this->atLeastOnce())->method('getQuote')->willReturn($this->quoteMock); $this->quoteMock->expects($this->atLeastOnce()) ->method('getCustomerTaxClassId') ->willReturn($customerTaxClassId); $this->quoteMock->expects($this->atLeastOnce())->method('getBillingAddress')->willReturn($this->addressMock); - + $this->shippingMock->expects($this->any())->method('getAddress')->willReturn($this->addressMock); $keyDataObjectMock = $this->getMockBuilder('\Magento\Tax\Api\Data\TaxClassKeyInterface') ->disableOriginalConstructor() ->getMockForAbstractClass(); diff --git a/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/TaxTest.php b/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/TaxTest.php index fc1f96e4364ab..e2259e548666c 100644 --- a/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/TaxTest.php +++ b/app/code/Magento/Tax/Test/Unit/Model/Sales/Total/Quote/TaxTest.php @@ -38,6 +38,8 @@ public function testCollect($itemData, $appliedRatesData, $taxDetailsData, $quot $addressData, $verifyData ) { $this->markTestIncomplete('Source code is not testable. Need to be refactored before unit testing'); + $shippingAssignmentMock = $this->getMock('Magento\Quote\Api\Data\ShippingAssignmentInterface'); + $totalsMock = $this->getMock('Magento\Quote\Model\Quote\Address\Total', [], [], '', false); $objectManager = new ObjectManager($this); $taxData = $this->getMock('Magento\Tax\Helper\Data', [], [], '', false); $taxConfig = $this->getMockBuilder('\Magento\Tax\Model\Config') @@ -271,7 +273,7 @@ public function testCollect($itemData, $appliedRatesData, $taxDetailsData, $quot $address->setData($key, $value); } - $taxTotalsCalcModel->collect($address); + $taxTotalsCalcModel->collect($quote, $shippingAssignmentMock, $totalsMock); foreach ($verifyData as $key => $value) { $this->assertSame($verifyData[$key], $address->getData($key)); } @@ -427,7 +429,6 @@ public function testMapQuoteExtraTaxables($itemData, $addressData) { $objectManager = new ObjectManager($this); $taxTotalsCalcModel = $objectManager->getObject('Magento\Tax\Model\Sales\Total\Quote\Tax'); - $taxClassKeyDataObjectMock = $this->getMock('\Magento\Tax\Api\Data\TaxClassKeyInterface'); $taxClassKeyDataObjectFactoryMock = $this->getMockBuilder('\Magento\Tax\Api\Data\TaxClassKeyInterfaceFactory') ->disableOriginalConstructor() @@ -565,10 +566,15 @@ public function dataProviderMapQuoteExtraTaxablesArray() */ public function testFetch($appliedTaxesData, $addressData) { + $methods = ['getAppliedTaxes', 'getTaxAmount', 'getTotalAmount', 'getGrandTotal', 'getSubtotalInclTax']; + $totalsMock = $this->getMock('Magento\Quote\Model\Quote\Address\Total', $methods, [], '', false); $taxConfig = $this->getMockBuilder('\Magento\Tax\Model\Config') ->disableOriginalConstructor() ->setMethods(['displayCartTaxWithGrandTotal', 'displayCartZeroTax', 'displayCartSubtotalBoth']) ->getMock(); + $shippingAssignmentMock = $this->getMock('Magento\Quote\Api\Data\ShippingAssignmentInterface'); + $shippingMock = $this->getMock('Magento\Quote\Api\Data\ShippingInterface'); + $shippingAssignmentMock->expects($this->any())->method('getShipping')->willReturn($shippingMock); $taxConfig ->expects($this->once()) ->method('displayCartTaxWithGrandTotal') @@ -579,6 +585,7 @@ public function testFetch($appliedTaxesData, $addressData) ->will($this->returnValue(true)); $objectManager = new ObjectManager($this); + /** @var \Magento\Tax\Model\Sales\Total\Quote\Tax $taxTotalsCalcModel */ $taxTotalsCalcModel = $objectManager->getObject( 'Magento\Tax\Model\Sales\Total\Quote\Tax', ['taxConfig' => $taxConfig] @@ -595,14 +602,15 @@ public function testFetch($appliedTaxesData, $addressData) $address = $this->getMock( '\Magento\Quote\Model\Quote\Address', [ - 'getAppliedTaxes', 'getQuote', 'getAllItems', 'getGrandTotal', '__wakeup', + 'getQuote', 'getAllItems', 'getGrandTotal', '__wakeup', 'addTotal', 'getTaxAmount' ], [], '', false ); - $address + $shippingMock->expects($this->any())->method('getAddress')->willReturn($address); + $totalsMock ->expects($this->once()) ->method('getAppliedTaxes') ->will($this->returnValue($appliedTaxes)); @@ -610,11 +618,7 @@ public function testFetch($appliedTaxesData, $addressData) ->expects($this->any()) ->method('getQuote') ->will($this->returnValue($quote)); - $address - ->expects($this->once()) - ->method('getAllItems') - ->will($this->returnValue($items)); - $address + $totalsMock ->expects($this->any()) ->method('getGrandTotal') ->will($this->returnValue(88)); @@ -632,7 +636,7 @@ public function testFetch($appliedTaxesData, $addressData) $address->setData($key, $value); } - $taxTotalsCalcModel->fetch($address); + $taxTotalsCalcModel->fetch($quote, $totalsMock); } /** @@ -681,6 +685,11 @@ public function testGetLabel() */ public function testEmptyAddress() { + $totalsMock = $this->getMock('Magento\Quote\Model\Quote\Address\Total', [], [], '', false); + $shippingAssignmentMock = $this->getMock('Magento\Quote\Api\Data\ShippingAssignmentInterface'); + $quote = $this->getMock('Magento\Quote\Model\Quote', [], [], '', false); + $shippingMock = $this->getMock('Magento\Quote\Api\Data\ShippingInterface'); + $shippingAssignmentMock->expects($this->any())->method('getShipping')->willReturn($shippingMock); /** @var $address \Magento\Quote\Model\Quote\Address|PHPUnit_Framework_MockObject_MockObject */ $address = $this->getMockBuilder('\Magento\Quote\Model\Quote\Address') ->disableOriginalConstructor() @@ -690,25 +699,25 @@ public function testEmptyAddress() '__wakeup', ] )->getMock(); - - $address->setTotalAmount('subtotal', 1); - $address->setBaseTotalAmount('subtotal', 1); - $address->setTotalAmount('tax', 1); - $address->setBaseTotalAmount('tax', 1); - $address->setTotalAmount('discount_tax_compensation', 1); - $address->setBaseTotalAmount('discount_tax_compensation', 1); - $address->setTotalAmount('shipping_discount_tax_compensation', 1); - $address->setBaseTotalAmount('shipping_discount_tax_compensation', 1); - $address->setSubtotalInclTax(1); - $address->setBaseSubtotalInclTax(1); - - $address->expects($this->once()) - ->method('getAllItems') + $shippingMock->expects($this->any())->method('getAddress')->willReturn($address); + $totalsMock->setTotalAmount('subtotal', 1); + $totalsMock->setBaseTotalAmount('subtotal', 1); + $totalsMock->setTotalAmount('tax', 1); + $totalsMock->setBaseTotalAmount('tax', 1); + $totalsMock->setTotalAmount('discount_tax_compensation', 1); + $totalsMock->setBaseTotalAmount('discount_tax_compensation', 1); + $totalsMock->setTotalAmount('shipping_discount_tax_compensation', 1); + $totalsMock->setBaseTotalAmount('shipping_discount_tax_compensation', 1); + $totalsMock->setSubtotalInclTax(1); + $totalsMock->setBaseSubtotalInclTax(1); + + $shippingAssignmentMock->expects($this->once()) + ->method('getItems') ->will($this->returnValue([])); $objectManager = new ObjectManager($this); $taxCollector = $objectManager->getObject('Magento\Tax\Model\Sales\Total\Quote\Tax'); - $taxCollector->collect($address); + $taxCollector->collect($quote, $shippingAssignmentMock, $totalsMock); $this->assertEquals(0, $address->getTotalAmount('subtotal')); $this->assertEquals(0, $address->getTotalAmount('tax')); diff --git a/app/code/Magento/Tax/etc/di.xml b/app/code/Magento/Tax/etc/di.xml index eb2c97bf8d7db..d2ba3241bf599 100644 --- a/app/code/Magento/Tax/etc/di.xml +++ b/app/code/Magento/Tax/etc/di.xml @@ -64,7 +64,7 @@ - + diff --git a/app/code/Magento/Tax/etc/events.xml b/app/code/Magento/Tax/etc/events.xml index 741076a1f5b7a..d9fc66fb1edbd 100644 --- a/app/code/Magento/Tax/etc/events.xml +++ b/app/code/Magento/Tax/etc/events.xml @@ -6,9 +6,6 @@ */ --> - - - diff --git a/app/code/Magento/Tax/etc/extension_attributes.xml b/app/code/Magento/Tax/etc/extension_attributes.xml index 810c04b8c1917..b00c7862c2f1d 100644 --- a/app/code/Magento/Tax/etc/extension_attributes.xml +++ b/app/code/Magento/Tax/etc/extension_attributes.xml @@ -6,7 +6,7 @@ */ --> - + diff --git a/app/code/Magento/Tax/view/frontend/templates/checkout/shipping.phtml b/app/code/Magento/Tax/view/frontend/templates/checkout/shipping.phtml index 605fd43d1b1c3..20400f906cebe 100644 --- a/app/code/Magento/Tax/view/frontend/templates/checkout/shipping.phtml +++ b/app/code/Magento/Tax/view/frontend/templates/checkout/shipping.phtml @@ -11,39 +11,41 @@ * @see \Magento\Tax\Block\Checkout\Shipping */ ?> -displayBoth()):?> - - - getExcludeTaxLabel() ?> - - - helper('Magento\Checkout\Helper\Data')->formatPrice($block->getShippingExcludeTax()) ?> - - - - - getIncludeTaxLabel() ?> - - - helper('Magento\Checkout\Helper\Data')->formatPrice($block->getShippingIncludeTax()) ?> - - -displayIncludeTax()) : ?> - - - getTotal()->getTitle() ?> - - - helper('Magento\Checkout\Helper\Data')->formatPrice($block->getShippingIncludeTax()) ?> - - - - - - escapeHtml($block->getTotal()->getTitle()) ?> - - - helper('Magento\Checkout\Helper\Data')->formatPrice($block->getShippingExcludeTax()) ?> - - - +displayShipping()):?> + displayBoth()):?> + + + getExcludeTaxLabel() ?> + + + helper('Magento\Checkout\Helper\Data')->formatPrice($block->getShippingExcludeTax()) ?> + + + + + getIncludeTaxLabel() ?> + + + helper('Magento\Checkout\Helper\Data')->formatPrice($block->getShippingIncludeTax()) ?> + + + displayIncludeTax()) : ?> + + + getTotal()->getTitle() ?> + + + helper('Magento\Checkout\Helper\Data')->formatPrice($block->getShippingIncludeTax()) ?> + + + + + + escapeHtml($block->getTotal()->getTitle()) ?> + + + helper('Magento\Checkout\Helper\Data')->formatPrice($block->getShippingExcludeTax()) ?> + + + + \ No newline at end of file diff --git a/app/code/Magento/Tax/view/frontend/templates/checkout/tax.phtml b/app/code/Magento/Tax/view/frontend/templates/checkout/tax.phtml index 5961f8abd0643..b3d71d381b5a3 100644 --- a/app/code/Magento/Tax/view/frontend/templates/checkout/tax.phtml +++ b/app/code/Magento/Tax/view/frontend/templates/checkout/tax.phtml @@ -39,7 +39,7 @@ helper('Magento\Tax\Helper\Data')->displayFullSummary() && $_value != 0): ?> - getTotal()->getFullInfo() as $info): ?> + getTotal()->getFullInfo()) as $info): ?> diff --git a/app/code/Magento/Tax/view/frontend/web/js/view/checkout/summary/tax.js b/app/code/Magento/Tax/view/frontend/web/js/view/checkout/summary/tax.js index 3418d3236b0ea..6f6f4dc18ed86 100644 --- a/app/code/Magento/Tax/view/frontend/web/js/view/checkout/summary/tax.js +++ b/app/code/Magento/Tax/view/frontend/web/js/view/checkout/summary/tax.js @@ -60,9 +60,9 @@ define( return this.getFormattedPrice(amount); }, getDetails: function() { - var totals = this.totals(); - if (totals.extension_attributes) { - return totals.extension_attributes.tax_grandtotal_details; + var taxSegment = totals.getSegment('tax'); + if (taxSegment && taxSegment.extension_attributes) { + return taxSegment.extension_attributes.tax_grandtotal_details; } return []; } diff --git a/app/code/Magento/Weee/Model/Total/Quote/Weee.php b/app/code/Magento/Weee/Model/Total/Quote/Weee.php index e3503881eb9a8..326c44070f90b 100644 --- a/app/code/Magento/Weee/Model/Total/Quote/Weee.php +++ b/app/code/Magento/Weee/Model/Total/Quote/Weee.php @@ -84,18 +84,24 @@ public function __construct( /** * Collect Weee amounts for the quote / order * - * @param \Magento\Quote\Model\Quote\Address $address - * @return $this + * @param \Magento\Quote\Model\Quote $quote + * @param \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment + * @param \Magento\Quote\Model\Quote\Address\Total $total + * @return $this */ - public function collect(\Magento\Quote\Model\Quote\Address $address) - { - AbstractTotal::collect($address); - $this->_store = $address->getQuote()->getStore(); + public function collect( + \Magento\Quote\Model\Quote $quote, + \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment, + \Magento\Quote\Model\Quote\Address\Total $total + ) { + AbstractTotal::collect($quote, $shippingAssignment, $total); + $this->_store = $quote->getStore(); if (!$this->weeeData->isEnabled($this->_store)) { return $this; } - $items = $this->_getAddressItems($address); + $address = $shippingAssignment->getShipping()->getAddress(); + $items = $shippingAssignment->getItems(); if (!count($items)) { return $this; } @@ -110,16 +116,16 @@ public function collect(\Magento\Quote\Model\Quote\Address $address) if ($item->getHasChildren() && $item->isChildrenCalculated()) { foreach ($item->getChildren() as $child) { $this->resetItemData($child); - $this->process($address, $child); + $this->process($address, $total, $child); } $this->recalculateParent($item); } else { - $this->process($address, $item); + $this->process($address, $total, $item); } } - $address->setWeeeCodeToItemMap($this->weeeCodeToItemMap); - $address->setWeeeTotalExclTax($this->weeeTotalExclTax); - $address->setWeeeBaseTotalExclTax($this->weeeBaseTotalExclTax); + $total->setWeeeCodeToItemMap($this->weeeCodeToItemMap); + $total->setWeeeTotalExclTax($this->weeeTotalExclTax); + $total->setWeeeBaseTotalExclTax($this->weeeBaseTotalExclTax); return $this; } @@ -127,13 +133,17 @@ public function collect(\Magento\Quote\Model\Quote\Address $address) * Calculate item fixed tax and prepare information for discount and regular taxation * * @param \Magento\Quote\Model\Quote\Address $address + * @param \Magento\Quote\Model\Quote\Address\Total $total * @param \Magento\Quote\Model\Quote\Item\AbstractItem $item * @return void|$this * @SuppressWarnings(PHPMD.ExcessiveMethodLength) * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ - protected function process(\Magento\Quote\Model\Quote\Address $address, $item) - { + protected function process( + \Magento\Quote\Model\Quote\Address $address, + \Magento\Quote\Model\Quote\Address\Total $total, + $item + ) { $attributes = $this->weeeData->getProductWeeeAttributes( $item->getProduct(), $address, @@ -227,7 +237,7 @@ protected function process(\Magento\Quote\Model\Quote\Address $address, $item) ->setBaseWeeeTaxAppliedRowAmntInclTax($baseTotalRowValueInclTax); $this->processTotalAmount( - $address, + $total, $totalRowValueExclTax, $baseTotalRowValueExclTax, $totalRowValueInclTax, @@ -240,7 +250,7 @@ protected function process(\Magento\Quote\Model\Quote\Address $address, $item) /** * Process row amount based on FPT total amount configuration setting * - * @param \Magento\Quote\Model\Quote\Address $address + * @param \Magento\Quote\Model\Quote\Address\Total $total * @param float $rowValueExclTax * @param float $baseRowValueExclTax * @param float $rowValueInclTax @@ -248,7 +258,7 @@ protected function process(\Magento\Quote\Model\Quote\Address $address, $item) * @return $this */ protected function processTotalAmount( - $address, + $total, $rowValueExclTax, $baseRowValueExclTax, $rowValueInclTax, @@ -261,11 +271,11 @@ protected function processTotalAmount( } //This value is used to calculate shipping cost; it will be overridden by tax collector - $address->setSubtotalInclTax( - $address->getSubtotalInclTax() + $this->priceCurrency->round($rowValueInclTax) + $total->setSubtotalInclTax( + $total->getSubtotalInclTax() + $this->priceCurrency->round($rowValueInclTax) ); - $address->setBaseSubtotalInclTax( - $address->getBaseSubtotalInclTax() + $this->priceCurrency->round($baseRowValueInclTax) + $total->setBaseSubtotalInclTax( + $total->getBaseSubtotalInclTax() + $this->priceCurrency->round($baseRowValueInclTax) ); return $this; } @@ -324,15 +334,14 @@ protected function resetItemData($item) } /** - * Delegate this to WeeeTax collector - * - * @param \Magento\Quote\Model\Quote\Address $address - * @return $this + * @param \Magento\Quote\Model\Quote $quote + * @param \Magento\Quote\Model\Quote\Address\Total $total + * @return array|null * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function fetch(\Magento\Quote\Model\Quote\Address $address) + public function fetch(\Magento\Quote\Model\Quote $quote, \Magento\Quote\Model\Quote\Address\Total $total) { - return $this; + return null; } /** diff --git a/app/code/Magento/Weee/Model/Total/Quote/WeeeTax.php b/app/code/Magento/Weee/Model/Total/Quote/WeeeTax.php index 3b6fb86fbec88..cc075754d219d 100644 --- a/app/code/Magento/Weee/Model/Total/Quote/WeeeTax.php +++ b/app/code/Magento/Weee/Model/Total/Quote/WeeeTax.php @@ -14,24 +14,25 @@ class WeeeTax extends Weee /** * Collect Weee taxes amount and prepare items prices for taxation and discount * - * @param \Magento\Quote\Model\Quote\Address $address - * @return $this + * @param \Magento\Quote\Model\Quote $quote + * @param \Magento\Quote\Api\Data\ShippingAssignmentInterface|\Magento\Quote\Model\Quote\Address $shippingAssignment + * @param \Magento\Quote\Model\Quote\Address\Total $total + * @return $this * @SuppressWarnings(PHPMD.ExcessiveMethodLength) * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ - public function collect(\Magento\Quote\Model\Quote\Address $address) - { - //Reset - \Magento\Quote\Model\Quote\Address\Total\AbstractTotal::collect($address); - - //Ensure Weee module is enabled - $this->_store = $address->getQuote()->getStore(); + public function collect( + \Magento\Quote\Model\Quote $quote, + \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment, + \Magento\Quote\Model\Quote\Address\Total $total + ) { + \Magento\Quote\Model\Quote\Address\Total\AbstractTotal::collect($quote, $shippingAssignment, $total); + $this->_store = $quote->getStore(); if (!$this->weeeData->isEnabled($this->_store)) { return $this; } - //Get all the items - $items = $this->_getAddressItems($address); + $items = $shippingAssignment->getItems(); if (!count($items)) { return $this; } @@ -39,19 +40,19 @@ public function collect(\Magento\Quote\Model\Quote\Address $address) //If Weee is not taxable, then the 'weee' collector has accumulated the non-taxable total values if (!$this->weeeData->isTaxable($this->_store)) { //Because Weee is not taxable: Weee excluding tax == Weee including tax - $weeeTotal = $address->getWeeeTotalExclTax(); - $weeeBaseTotal = $address->getWeeeBaseTotalExclTax(); + $weeeTotal = $total->getWeeeTotalExclTax(); + $weeeBaseTotal = $total->getWeeeBaseTotalExclTax(); //Add to appropriate 'subtotal' or 'weee' accumulators - $this->processTotalAmount($address, $weeeTotal, $weeeBaseTotal, $weeeTotal, $weeeBaseTotal); + $this->processTotalAmount($total, $weeeTotal, $weeeBaseTotal, $weeeTotal, $weeeBaseTotal); return $this; } + + $extraTaxableDetails = $total->getExtraTaxableDetails(); - //Ensure we have taxable weee data - $extraTaxableDetails = $address->getExtraTaxableDetails(); if (isset($extraTaxableDetails[self::ITEM_TYPE])) { //Get mapping from weeeCode to item - $weeeCodeToItemMap = $address->getWeeeCodeToItemMap(); + $weeeCodeToItemMap = $total->getWeeeCodeToItemMap(); //Create mapping from item to weeeCode $itemToWeeeCodeMap = $this->createItemToWeeeCodeMapping($weeeCodeToItemMap); @@ -135,7 +136,7 @@ public function collect(\Magento\Quote\Model\Quote\Address $address) ->setBaseWeeeTaxAppliedRowAmntInclTax($baseTotalRowValueInclTax); $this->processTotalAmount( - $address, + $total, $totalRowValueExclTax, $baseTotalRowValueExclTax, $totalRowValueInclTax, @@ -145,7 +146,6 @@ public function collect(\Magento\Quote\Model\Quote\Address $address) $this->weeeData->setApplied($item, array_merge($this->weeeData->getApplied($item), $productTaxes)); } } - return $this; } @@ -196,56 +196,54 @@ protected function createItemToWeeeCodeMapping($weeeCodeToItemMap) /** * Process row amount based on FPT total amount configuration setting * - * @param \Magento\Quote\Model\Quote\Address $address - * @param float $rowValueExclTax - * @param float $baseRowValueExclTax - * @param float $rowValueInclTax - * @param float $baseRowValueInclTax - * @return $this + * @param \Magento\Quote\Model\Quote\Address\Total $total + * @param float $rowValueExclTax + * @param float $baseRowValueExclTax + * @param float $rowValueInclTax + * @param float $baseRowValueInclTax + * @return $this */ protected function processTotalAmount( - $address, + $total, $rowValueExclTax, $baseRowValueExclTax, $rowValueInclTax, $baseRowValueInclTax ) { if ($this->weeeData->includeInSubtotal($this->_store)) { - $address->addTotalAmount('subtotal', $rowValueExclTax); - $address->addBaseTotalAmount('subtotal', $baseRowValueExclTax); + $total->addTotalAmount('subtotal', $rowValueExclTax); + $total->addBaseTotalAmount('subtotal', $baseRowValueExclTax); } else { - $address->addTotalAmount('weee', $rowValueExclTax); - $address->addBaseTotalAmount('weee', $baseRowValueExclTax); + $total->addTotalAmount('weee', $rowValueExclTax); + $total->addBaseTotalAmount('weee', $baseRowValueExclTax); } - $address->setSubtotalInclTax($address->getSubtotalInclTax() + $rowValueInclTax); - $address->setBaseSubtotalInclTax($address->getBaseSubtotalInclTax() + $baseRowValueInclTax); + $total->setSubtotalInclTax($total->getSubtotalInclTax() + $rowValueInclTax); + $total->setBaseSubtotalInclTax($total->getBaseSubtotalInclTax() + $baseRowValueInclTax); return $this; } /** * Fetch the Weee total amount for display in totals block when building the initial quote * - * @param \Magento\Quote\Model\Quote\Address $address - * @return $this + * @param \Magento\Quote\Model\Quote $quote + * @param \Magento\Quote\Model\Quote\Address\Total $total + * @return array */ - public function fetch(\Magento\Quote\Model\Quote\Address $address) + public function fetch(\Magento\Quote\Model\Quote $quote, \Magento\Quote\Model\Quote\Address\Total $total) { /** @var $items \Magento\Sales\Model\Order\Item[] */ - $items = $this->_getAddressItems($address); - $store = $address->getQuote()->getStore(); + $items = isset($total['address_quote_items']) ? $total['address_quote_items'] : []; - $weeeTotal = $this->weeeData->getTotalAmounts($items, $store); + $weeeTotal = $this->weeeData->getTotalAmounts($items, $quote->getStore()); if ($weeeTotal) { - $address->addTotal( - [ - 'code' => $this->getCode(), - 'title' => __('FPT'), - 'value' => $weeeTotal, - 'area' => null, - ] - ); + return [ + 'code' => $this->getCode(), + 'title' => __('FPT'), + 'value' => $weeeTotal, + 'area' => null, + ]; } - return $this; + return null; } } diff --git a/app/code/Magento/Weee/Test/Unit/Model/Total/Quote/WeeeTaxTest.php b/app/code/Magento/Weee/Test/Unit/Model/Total/Quote/WeeeTaxTest.php index 2ee47eaf56b27..7070cf8942f6c 100644 --- a/app/code/Magento/Weee/Test/Unit/Model/Total/Quote/WeeeTaxTest.php +++ b/app/code/Magento/Weee/Test/Unit/Model/Total/Quote/WeeeTaxTest.php @@ -17,6 +17,27 @@ class WeeeTaxTest extends \PHPUnit_Framework_TestCase const KEY_WEEE_BASE_TOTALS = 'weee_base_total_excl_tax'; /**#@-*/ + /** + * @var \Magento\Weee\Model\Total\Quote\WeeeTax + */ + protected $weeeCollector; + + /** + * @var \PHPUnit_FrameWork_MockObject_MockObject | \Magento\Quote\Model\Quote + */ + protected $quoteMock; + + /** + * \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + */ + protected $objectManagerHelper; + + public function setUp() + { + $this->objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->quoteMock = $this->getMock('\Magento\Quote\Model\Quote', [], [], '', false); + } + /** * Setup tax helper with an array of methodName, returnValue * @@ -89,14 +110,12 @@ protected function setupItemMock($itemQty) * @param array $addressData * @return \PHPUnit_Framework_MockObject_MockObject */ - protected function setupAddressMock($itemMock, $isWeeeTaxable, $itemWeeeTaxDetails, $addressData) + protected function setupTotalMock($itemMock, $isWeeeTaxable, $itemWeeeTaxDetails, $addressData) { - $addressMock = $this->getMock( - 'Magento\Quote\Model\Quote\Address', + $totalMock = $this->getMock( + '\Magento\Quote\Model\Quote\Address\Total', [ '__wakeup', - 'getAllItems', - 'getQuote', 'getWeeeCodeToItemMap', 'getExtraTaxableDetails', 'getWeeeTotalExclTax', @@ -148,35 +167,55 @@ protected function setupAddressMock($itemMock, $isWeeeTaxable, $itemWeeeTaxDetai } } - $quoteMock = $this->getMock('Magento\Quote\Model\Quote', [], [], '', false); - $storeMock = $this->getMock('Magento\Store\Model\Store', ['__wakeup', 'convertPrice'], [], '', false); - $storeMock->expects($this->any())->method('convertPrice')->will($this->returnArgument(0)); - $quoteMock->expects($this->any())->method('getStore')->will($this->returnValue($storeMock)); - - $addressMock->expects($this->any())->method('getAllItems')->will($this->returnValue([$itemMock])); - $addressMock->expects($this->any())->method('getQuote')->will($this->returnValue($quoteMock)); - $addressMock->expects($this->any())->method('getWeeeCodeToItemMap')->will($this->returnValue($map)); - $addressMock->expects($this->any())->method('getExtraTaxableDetails')->will($this->returnValue($extraDetails)); - $addressMock + $totalMock->expects($this->any())->method('getWeeeCodeToItemMap')->will($this->returnValue($map)); + $totalMock->expects($this->any())->method('getExtraTaxableDetails')->will($this->returnValue($extraDetails)); + $totalMock ->expects($this->any()) ->method('getWeeeTotalExclTax') ->will($this->returnValue($weeeTotals)); - $addressMock + $totalMock ->expects($this->any()) ->method('getWeeeBaseTotalExclTax') ->will($this->returnValue($weeeBaseTotals)); - return $addressMock; + return $totalMock; + } + + /** + * Setup shipping assignment mock. + * @param \PHPUnit_Framework_MockObject_MockObject $addressMock + * @param \PHPUnit_Framework_MockObject_MockObject $itemMock + * @return \PHPUnit_Framework_MockObject_MockObject + */ + protected function setupShippingAssignmentMock($addressMock, $itemMock) + { + $shippingMock = $this->getMock('\Magento\Quote\Api\Data\ShippingInterface', [], [], '', false); + $shippingMock->expects($this->any())->method('getAddress')->willReturn($addressMock); + $shippingAssignmentMock = $this->getMock( + '\Magento\Quote\Api\Data\ShippingAssignmentInterface', + [], + [], + '', + false + ); + $itemMock = $itemMock ? [$itemMock] : []; + $shippingAssignmentMock->expects($this->any())->method('getItems')->willReturn($itemMock); + $shippingAssignmentMock->expects($this->any())->method('getShipping')->willReturn($shippingMock); + + return $shippingAssignmentMock; } /** * Verify that correct fields of item has been set * - * @param \PHPUnit_Framework_MockObject_MockObject|\Magento\Quote\Model\Quote\Item $item + * @param \PHPUnit_Framework_MockObject_MockObject|null $item * @param array $itemData */ - public function verifyItem(\Magento\Quote\Model\Quote\Item $item, $itemData) + public function verifyItem($item, $itemData) { + if (!$item) { + return; + } foreach ($itemData as $key => $value) { $this->assertEquals($value, $item->getData($key), 'item ' . $key . ' is incorrect'); } @@ -188,7 +227,7 @@ public function verifyItem(\Magento\Quote\Model\Quote\Item $item, $itemData) * @param \PHPUnit_Framework_MockObject_MockObject|\Magento\Quote\Model\Quote\Address $address * @param array $addressData */ - public function verifyAddress(\Magento\Quote\Model\Quote\Address $address, $addressData) + public function verifyTotals($address, $addressData) { foreach ($addressData as $key => $value) { if ($key != self::KEY_WEEE_TOTALS && $key != self::KEY_WEEE_BASE_TOTALS) { @@ -198,6 +237,39 @@ public function verifyAddress(\Magento\Quote\Model\Quote\Address $address, $addr } } + public function testFetch() + { + $weeeTotal = 17; + $totalMock = new \Magento\Quote\Model\Quote\Address\Total(); + $taxHelper = $this->setupTaxHelper([]); + $weeeHelper = $this->setupWeeeHelper(['getTotalAmounts' => $weeeTotal]); + $this->weeeCollector = $this->objectManagerHelper->getObject( + 'Magento\Weee\Model\Total\Quote\WeeeTax', + ['taxData' => $taxHelper, 'weeeData' => $weeeHelper] + ); + $expectedResult = [ + 'code' => 'weee', + 'title' => __('FPT'), + 'value' => $weeeTotal, + 'area' => null, + ]; + + $this->assertEquals($expectedResult, $this->weeeCollector->fetch($this->quoteMock, $totalMock)); + } + + public function testFetchWithZeroAmounts() + { + $totalMock = new \Magento\Quote\Model\Quote\Address\Total(); + $taxHelper = $this->setupTaxHelper([]); + $weeeHelper = $this->setupWeeeHelper(['getTotalAmounts' => null]); + $this->weeeCollector = $this->objectManagerHelper->getObject( + 'Magento\Weee\Model\Total\Quote\WeeeTax', + ['taxData' => $taxHelper, 'weeeData' => $weeeHelper] + ); + + $this->assertNull($this->weeeCollector->fetch($this->quoteMock, $totalMock)); + } + /** * Test the collect function of the weee collector * @@ -211,8 +283,14 @@ public function verifyAddress(\Magento\Quote\Model\Quote\Address $address, $addr public function testCollect($taxConfig, $weeeConfig, $itemWeeeTaxDetails, $itemQty, $addressData = []) { //Setup - $itemMock = $this->setupItemMock($itemQty); - $addressMock = $this->setupAddressMock($itemMock, $weeeConfig['isTaxable'], $itemWeeeTaxDetails, $addressData); + if ($itemQty > 0) { + $itemMock = $this->setupItemMock($itemQty); + } else { + $itemMock = null; + } + $totalMock = $this->setupTotalMock($itemMock, $weeeConfig['isTaxable'], $itemWeeeTaxDetails, $addressData); + $addressMock = $this->getMock('\Magento\Quote\Model\Quote\Address', [], [], '', false); + $shippingAssignmentMock = $this->setupShippingAssignmentMock($addressMock, $itemMock); $taxHelper = $this->setupTaxHelper($taxConfig); $weeeHelper = $this->setupWeeeHelper($weeeConfig); @@ -222,11 +300,13 @@ public function testCollect($taxConfig, $weeeConfig, $itemWeeeTaxDetails, $itemQ 'weeeData' => $weeeHelper, ]; - $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->weeeCollector = $helper->getObject('Magento\Weee\Model\Total\Quote\WeeeTax', $arguments); + $this->weeeCollector = $this->objectManagerHelper->getObject( + 'Magento\Weee\Model\Total\Quote\WeeeTax', + $arguments + ); //Execute - $this->weeeCollector->collect($addressMock); + $this->weeeCollector->collect($this->quoteMock, $shippingAssignmentMock, $totalMock); //Verify $summed = []; @@ -237,7 +317,7 @@ public function testCollect($taxConfig, $weeeConfig, $itemWeeeTaxDetails, $itemQ } $this->verifyItem($itemMock, $summed); - $this->verifyAddress($addressMock, $addressData); + $this->verifyTotals($totalMock, $addressData); } /** @@ -574,6 +654,94 @@ public function collectDataProvider() ], ]; + $data['weee_disabled'] = [ + 'tax_config' => [ + 'priceIncludesTax' => false, + 'getCalculationAlgorithm' => Calculation::CALC_UNIT_BASE, + ], + 'weee_config' => [ + 'isEnabled' => false, + 'includeInSubtotal' => false, + 'isTaxable' => true, + 'getApplied' => [], + ], + 'item_weee_tax_details' => [ + [ + 'weee_tax_applied_amount' => null, + 'base_weee_tax_applied_amount' => null, + 'weee_tax_applied_row_amount' => null, + 'base_weee_tax_applied_row_amnt' => null, + 'weee_tax_applied_amount_incl_tax' => null, + 'base_weee_tax_applied_amount_incl_tax' => null, + 'weee_tax_applied_row_amount_incl_tax' => null, + 'base_weee_tax_applied_row_amnt_incl_tax' => null, + ], + [ + 'weee_tax_applied_amount' => null, + 'base_weee_tax_applied_amount' => null, + 'weee_tax_applied_row_amount' => null, + 'base_weee_tax_applied_row_amnt' => null, + 'weee_tax_applied_amount_incl_tax' => null, + 'base_weee_tax_applied_amount_incl_tax' => null, + 'weee_tax_applied_row_amount_incl_tax' => null, + 'base_weee_tax_applied_row_amnt_incl_tax' => null, + ], + ], + 'item_qty' => 1, + 'address_data' => [ + 'subtotal' => null, + 'base_subtotal' => null, + 'subtotal_incl_tax' => null, + 'base_subtotal_incl_tax' => null, + 'weee_amount' => null, + 'base_weee_amount' => null, + ], + ]; + + $data['zero_items'] = [ + 'tax_config' => [ + 'priceIncludesTax' => false, + 'getCalculationAlgorithm' => Calculation::CALC_UNIT_BASE, + ], + 'weee_config' => [ + 'isEnabled' => true, + 'includeInSubtotal' => false, + 'isTaxable' => true, + 'getApplied' => [], + ], + 'item_weee_tax_details' => [ + [ + 'weee_tax_applied_amount' => null, + 'base_weee_tax_applied_amount' => null, + 'weee_tax_applied_row_amount' => null, + 'base_weee_tax_applied_row_amnt' => null, + 'weee_tax_applied_amount_incl_tax' => null, + 'base_weee_tax_applied_amount_incl_tax' => null, + 'weee_tax_applied_row_amount_incl_tax' => null, + 'base_weee_tax_applied_row_amnt_incl_tax' => null, + ], + [ + 'weee_tax_applied_amount' => null, + 'base_weee_tax_applied_amount' => null, + 'weee_tax_applied_row_amount' => null, + 'base_weee_tax_applied_row_amnt' => null, + 'weee_tax_applied_amount_incl_tax' => null, + 'base_weee_tax_applied_amount_incl_tax' => null, + 'weee_tax_applied_row_amount_incl_tax' => null, + 'base_weee_tax_applied_row_amnt_incl_tax' => null, + ], + ], + 'item_qty' => 0, + 'address_data' => [ + 'subtotal' => null, + 'base_subtotal' => null, + 'subtotal_incl_tax' => null, + 'base_subtotal_incl_tax' => null, + 'weee_amount' => null, + 'base_weee_amount' => null, + ], + ]; + return $data; } } diff --git a/app/code/Magento/Weee/Test/Unit/Model/Total/Quote/WeeeTest.php b/app/code/Magento/Weee/Test/Unit/Model/Total/Quote/WeeeTest.php index a7027d370367c..522b1cba2093e 100644 --- a/app/code/Magento/Weee/Test/Unit/Model/Total/Quote/WeeeTest.php +++ b/app/code/Magento/Weee/Test/Unit/Model/Total/Quote/WeeeTest.php @@ -14,6 +14,11 @@ class WeeeTest extends \PHPUnit_Framework_TestCase */ protected $priceCurrency; + /** + * @var \Magento\Weee\Model\Total\Quote\Weee + */ + protected $weeeCollector; + /** * Setup tax helper with an array of methodName, returnValue * @@ -191,6 +196,29 @@ protected function setupAddressMock($items) return $addressMock; } + /** + * Setup shipping assignment mock. + * @param \PHPUnit_Framework_MockObject_MockObject $addressMock + * @param \PHPUnit_Framework_MockObject_MockObject $itemMock + * @return \PHPUnit_Framework_MockObject_MockObject + */ + protected function setupShippingAssignmentMock($addressMock, $itemMock) + { + $shippingMock = $this->getMock('\Magento\Quote\Api\Data\ShippingInterface', [], [], '', false); + $shippingMock->expects($this->any())->method('getAddress')->willReturn($addressMock); + $shippingAssignmentMock = $this->getMock( + '\Magento\Quote\Api\Data\ShippingAssignmentInterface', + [], + [], + '', + false + ); + $shippingAssignmentMock->expects($this->any())->method('getItems')->willReturn($itemMock); + $shippingAssignmentMock->expects($this->any())->method('getShipping')->willReturn($shippingMock); + + return $shippingAssignmentMock; + } + /** * Verify that correct fields of item has been set * @@ -210,7 +238,7 @@ public function verifyItem(\Magento\Quote\Model\Quote\Item $item, $itemData) * @param \PHPUnit_Framework_MockObject_MockObject|\Magento\Quote\Model\Quote\Address $address * @param $addressData */ - public function verifyAddress(\Magento\Quote\Model\Quote\Address $address, $addressData) + public function verifyAddress($address, $addressData) { foreach ($addressData as $key => $value) { $this->assertEquals($value, $address->getData($key), 'address ' . $key . ' is incorrect'); @@ -247,7 +275,12 @@ public function testCollect( $itemMock = $this->setupItemMock($itemQty); $items[] = $itemMock; } + $quoteMock = $this->getMock('\Magento\Quote\Model\Quote', [], [], '', false); + $storeMock = $this->getMock('Magento\Store\Model\Store', [], [], '', false); + $quoteMock->expects($this->any())->method('getStore')->will($this->returnValue($storeMock)); $addressMock = $this->setupAddressMock($items); + $totalMock = new \Magento\Quote\Model\Quote\Address\Total(); + $shippingAssignmentMock = $this->setupShippingAssignmentMock($addressMock, $items); $taxHelper = $this->setupTaxHelper($taxConfig); $weeeHelper = $this->setupWeeeHelper($weeeConfig); @@ -297,16 +330,16 @@ public function testCollect( 'taxData' => $taxHelper, 'calculation' => $calculator, 'weeeData' => $weeeHelper, - 'priceCurrency' => $this->priceCurrency, + 'priceCurrency' => $this->priceCurrency ]; $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->weeeCollector = $helper->getObject('Magento\Weee\Model\Total\Quote\Weee', $arguments); - $this->weeeCollector->collect($addressMock); + $this->weeeCollector->collect($quoteMock, $shippingAssignmentMock, $totalMock); $this->verifyItem(end($items), $itemData); // verify the (child) item - $this->verifyAddress($addressMock, $addressData); + $this->verifyAddress($totalMock, $addressData); } /** diff --git a/app/code/Magento/Weee/view/frontend/web/js/view/checkout/summary/weee.js b/app/code/Magento/Weee/view/frontend/web/js/view/checkout/summary/weee.js index b6ab97b04ccc0..a4582e1b72911 100644 --- a/app/code/Magento/Weee/view/frontend/web/js/view/checkout/summary/weee.js +++ b/app/code/Magento/Weee/view/frontend/web/js/view/checkout/summary/weee.js @@ -23,7 +23,7 @@ define( * @returns {Number} */ getWeeeTaxSegment: function () { - var weee = totals.getSegment('weee_tax'); + var weee = totals.getSegment('weee_tax') || totals.getSegment('weee'); if (weee !== null && weee.hasOwnProperty('value')) { return weee.value; diff --git a/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestShippingMethodManagementTest.php b/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestShippingMethodManagementTest.php index d329d3e7ff2c4..6dbbc52e76ed7 100644 --- a/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestShippingMethodManagementTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestShippingMethodManagementTest.php @@ -25,10 +25,16 @@ class GuestShippingMethodManagementTest extends WebapiAbstract */ protected $quote; + /** + * @var \Magento\Quote\Model\Quote\TotalsCollector + */ + protected $totalsCollector; + protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); $this->quote = $this->objectManager->create('Magento\Quote\Model\Quote'); + $this->totalsCollector = $this->objectManager->create('Magento\Quote\Model\Quote\TotalsCollector'); } protected function getServiceInfo() @@ -64,7 +70,8 @@ public function testSetMethod() $cartId = $quoteIdMask->getMaskedId(); $shippingAddress = $this->quote->getShippingAddress(); $shippingAddress->setCollectShippingRates(true); - $shippingAddress->collectTotals()->save(); + $this->totalsCollector->collectAddressTotals($this->quote, $shippingAddress); + $shippingAddress->save(); $requestData = [ 'cartId' => $cartId, 'carrierCode' => 'flatrate', @@ -166,7 +173,8 @@ public function testGetMethod() $shippingAddress = $quote->getShippingAddress(); $shippingAddress->setCollectShippingRates(true); - $shippingAddress->collectTotals()->save(); + $this->totalsCollector->collectAddressTotals($this->quote, $shippingAddress); + $shippingAddress->save(); list($carrierCode, $methodCode) = explode('_', $shippingAddress->getShippingMethod()); list($carrierTitle, $methodTitle) = explode(' - ', $shippingAddress->getShippingDescription()); $data = [ diff --git a/dev/tests/api-functional/testsuite/Magento/Quote/Api/ShippingMethodManagementTest.php b/dev/tests/api-functional/testsuite/Magento/Quote/Api/ShippingMethodManagementTest.php index 2246a1bd2ac08..a1ea0c098f373 100644 --- a/dev/tests/api-functional/testsuite/Magento/Quote/Api/ShippingMethodManagementTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Quote/Api/ShippingMethodManagementTest.php @@ -25,10 +25,16 @@ class ShippingMethodManagementTest extends WebapiAbstract */ protected $quote; + /** + * @var \Magento\Quote\Model\Quote\TotalsCollector + */ + protected $totalsCollector; + protected function setUp() { $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); $this->quote = $this->objectManager->create('Magento\Quote\Model\Quote'); + $this->totalsCollector = $this->objectManager->create('Magento\Quote\Model\Quote\TotalsCollector'); } protected function getServiceInfo() @@ -55,7 +61,8 @@ public function testSetMethod() $serviceInfo = $this->getServiceInfo(); $shippingAddress = $this->quote->getShippingAddress(); $shippingAddress->setCollectShippingRates(true); - $shippingAddress->collectTotals()->save(); + $this->totalsCollector->collectAddressTotals($this->quote, $shippingAddress); + $shippingAddress->save(); $requestData = [ 'cartId' => $this->quote->getId(), 'carrierCode' => 'flatrate', @@ -150,7 +157,8 @@ public function testSetMethodForMyCart() ]; // cartId 999 will be overridden $shippingAddress = $this->quote->getShippingAddress(); $shippingAddress->setCollectShippingRates(true); - $shippingAddress->collectTotals()->save(); + $this->totalsCollector->collectAddressTotals($this->quote, $shippingAddress); + $shippingAddress->save(); $result = $this->_webApiCall($serviceInfo, $requestData); $this->assertEquals(true, $result); @@ -179,7 +187,8 @@ public function testGetMethod() $shippingAddress = $this->quote->getShippingAddress(); $shippingAddress->setCollectShippingRates(true); - $shippingAddress->collectTotals()->save(); + $this->totalsCollector->collectAddressTotals($this->quote, $shippingAddress); + $shippingAddress->save(); list($carrierCode, $methodCode) = explode('_', $shippingAddress->getShippingMethod()); list($carrierTitle, $methodTitle) = explode(' - ', $shippingAddress->getShippingDescription()); $shippingMethod = $shippingMethodManagementService->get($this->quote->getId()); @@ -247,7 +256,8 @@ public function testGetMethodForMyCart() /** @var \Magento\Quote\Api\ShippingMethodManagementInterface $shippingMethodManagementService */ $shippingAddress = $this->quote->getShippingAddress(); $shippingAddress->setCollectShippingRates(true); - $shippingAddress->collectTotals()->save(); + $this->totalsCollector->collectAddressTotals($this->quote, $shippingAddress); + $shippingAddress->save(); $serviceInfo = [ 'rest' => [ diff --git a/dev/tests/integration/testsuite/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php b/dev/tests/integration/testsuite/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php index 7c81cc8620c0f..78b33a66222c6 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Observer/Frontend/Quote/Address/CollectTotalsObserverTest.php @@ -5,6 +5,8 @@ */ namespace Magento\Quote\Observer\Frontend\Quote\Address; +use Magento\TestFramework\Helper\Bootstrap; + class CollectTotalsObserverTest extends \PHPUnit_Framework_TestCase { /** @@ -12,9 +14,17 @@ class CollectTotalsObserverTest extends \PHPUnit_Framework_TestCase */ protected $model; + /** + * Object Manager + * + * @var \Magento\Framework\ObjectManagerInterface + */ + private $objectManager; + protected function setUp() { - $this->model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + $this->objectManager = Bootstrap::getObjectManager(); + $this->model = $this->objectManager->create( 'Magento\Quote\Observer\Frontend\Quote\Address\CollectTotalsObserver' ); } @@ -49,10 +59,21 @@ public function testChangeQuoteCustomerGroupIdForCustomerWithDisabledAutomaticGr $quote->setCustomer($customerData); $quoteAddress = $quote->getBillingAddress(); + $shippingAssignment = $this->objectManager->create('Magento\Quote\Model\ShippingAssignment'); + $shipping = $this->objectManager->create('Magento\Quote\Model\Shipping'); + $shipping->setAddress($quoteAddress); + $shippingAssignment->setShipping($shipping); + /** @var \Magento\Quote\Model\Quote\Address\Total $total */ + $total = $this->objectManager->create('Magento\Quote\Model\Quote\Address\Total'); $eventObserver = $objectManager->create( 'Magento\Framework\Event\Observer', - ['data' => ['quote_address' => $quoteAddress]] + ['data' => [ + 'quote' => $quote, + 'shipping_assignment' => $shippingAssignment, + 'total' => $total + ] + ] ); $this->model->execute($eventObserver); @@ -94,9 +115,21 @@ public function testChangeQuoteCustomerGroupIdForCustomerWithEnabledAutomaticGro $quoteAddress = $quote->getBillingAddress(); + $shippingAssignment = $this->objectManager->create('Magento\Quote\Model\ShippingAssignment'); + $shipping = $this->objectManager->create('Magento\Quote\Model\Shipping'); + $shipping->setAddress($quoteAddress); + $shippingAssignment->setShipping($shipping); + /** @var \Magento\Quote\Model\Quote\Address\Total $total */ + $total = $this->objectManager->create('Magento\Quote\Model\Quote\Address\Total'); + $eventObserver = $objectManager->create( 'Magento\Framework\Event\Observer', - ['data' => ['quote_address' => $quoteAddress]] + ['data' => [ + 'quote' => $quote, + 'shipping_assignment' => $shippingAssignment, + 'total' => $total + ] + ] ); $this->model->execute($eventObserver); diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SubtotalTest.php b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SubtotalTest.php index a41ead2e6155b..061532ef4748d 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SubtotalTest.php +++ b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SubtotalTest.php @@ -8,7 +8,6 @@ namespace Magento\Tax\Model\Sales\Total\Quote; -use Magento\Tax\Model\ClassModel; use Magento\TestFramework\Helper\Bootstrap; /** @@ -99,18 +98,25 @@ public function testCollectUnitBased($expected) $quantity ); $address = $quote->getShippingAddress(); - + /** @var \Magento\Quote\Model\ShippingAssignment $shippingAssignment */ + $shippingAssignment = $this->objectManager->create('Magento\Quote\Model\ShippingAssignment'); + $shipping = $this->objectManager->create('Magento\Quote\Model\Shipping'); + $shipping->setAddress($address); + $shippingAssignment->setShipping($shipping); + $shippingAssignment->setItems($address->getAllItems()); + /** @var \Magento\Quote\Model\Quote\Address\Total $total */ + $total = $this->objectManager->create('Magento\Quote\Model\Quote\Address\Total'); /** @var \Magento\Quote\Model\Quote\Address\Total\Subtotal $addressSubtotalCollector */ $addressSubtotalCollector = $this->objectManager->create('Magento\Quote\Model\Quote\Address\Total\Subtotal'); - $addressSubtotalCollector->collect($address); + $addressSubtotalCollector->collect($quote, $shippingAssignment, $total); /** @var \Magento\Tax\Model\Sales\Total\Quote\Subtotal $subtotalCollector */ $subtotalCollector = $this->objectManager->create('Magento\Tax\Model\Sales\Total\Quote\Subtotal'); - $subtotalCollector->collect($address); + $subtotalCollector->collect($quote, $shippingAssignment, $total); - $this->assertEquals($expected['subtotal'], $address->getSubtotal()); - $this->assertEquals($expected['subtotal'] + $expected['tax_amount'], $address->getSubtotalInclTax()); - $this->assertEquals($expected['discount_amount'], $address->getDiscountAmount()); + $this->assertEquals($expected['subtotal'], $total->getSubtotal()); + $this->assertEquals($expected['subtotal'] + $expected['tax_amount'], $total->getSubtotalInclTax()); + $this->assertEquals($expected['discount_amount'], $total->getDiscountAmount()); $items = $address->getAllItems(); /** @var \Magento\Quote\Model\Quote\Address\Item $item */ $item = $items[0]; @@ -214,18 +220,25 @@ public function testCollectUnitBasedBundleProduct($expected) $quantity ); $address = $quote->getShippingAddress(); - + /** @var \Magento\Quote\Model\ShippingAssignment $shippingAssignment */ + $shippingAssignment = $this->objectManager->create('Magento\Quote\Model\ShippingAssignment'); + $shipping = $this->objectManager->create('Magento\Quote\Model\Shipping'); + $shipping->setAddress($address); + $shippingAssignment->setShipping($shipping); + $shippingAssignment->setItems($quote->getAllItems()); + /** @var \Magento\Quote\Model\Quote\Address\Total $total */ + $total = $this->objectManager->create('Magento\Quote\Model\Quote\Address\Total'); /** @var \Magento\Quote\Model\Quote\Address\Total\Subtotal $addressSubtotalCollector */ $addressSubtotalCollector = $this->objectManager->create('Magento\Quote\Model\Quote\Address\Total\Subtotal'); - $addressSubtotalCollector->collect($address); + $addressSubtotalCollector->collect($quote, $shippingAssignment, $total); /** @var \Magento\Tax\Model\Sales\Total\Quote\Subtotal $subtotalCollector */ $subtotalCollector = $this->objectManager->create('Magento\Tax\Model\Sales\Total\Quote\Subtotal'); - $subtotalCollector->collect($address); + $subtotalCollector->collect($quote, $shippingAssignment, $total); - $this->assertEquals($expected['subtotal'], $address->getSubtotal()); - $this->assertEquals($expected['subtotal'] + $expected['tax_amount'], $address->getSubtotalInclTax()); - $this->assertEquals($expected['discount_amount'], $address->getDiscountAmount()); + $this->assertEquals($expected['subtotal'], $total->getSubtotal()); + $this->assertEquals($expected['subtotal'] + $expected['tax_amount'], $total->getSubtotalInclTax()); + $this->assertEquals($expected['discount_amount'], $total->getDiscountAmount()); $items = $address->getAllItems(); /** @var \Magento\Quote\Model\Quote\Address\Item $item */ $item = $items[0]; diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php index 6d8592bc31670..90f634de7d7f5 100644 --- a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php +++ b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/TaxTest.php @@ -236,6 +236,8 @@ public function testTaxCalculation($configData, $quoteData, $expectedResults) { /** @var \Magento\Framework\ObjectManagerInterface $objectManager */ $objectManager = Bootstrap::getObjectManager(); + /** @var \Magento\Quote\Model\Quote\TotalsCollector $totalsCollector */ + $totalsCollector = $objectManager->create('Magento\Quote\Model\Quote\TotalsCollector'); //Setup tax configurations $this->setupUtil = new SetupUtil($objectManager); @@ -243,8 +245,7 @@ public function testTaxCalculation($configData, $quoteData, $expectedResults) $quote = $this->setupUtil->setupQuote($quoteData); $quoteAddress = $quote->getShippingAddress(); - - $quoteAddress->collectTotals(); + $totalsCollector->collectAddressTotals($quote, $quoteAddress); $this->verifyResult($quoteAddress, $expectedResults); } diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php index ea2e70e21cc4d..8a8b52f324bf8 100755 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php @@ -3847,6 +3847,9 @@ ['Magento\Setup\Model\SampleData', 'Magento\SampleData\Model\SampleData'], ['Magento\Customer\Controller\Account\ResetPassword'], ['Magento\Customer\Controller\Account'], + ['Magento\Quote\Model\Quote\Address\Total\Discount'], + ['Magento\Quote\Model\Quote\Address\Total\Custbalance'], + ['Magento\Quote\Model\Quote\Address\Total\Tax'], ['Magento\ConfigurableProduct\Block\Product\Configurable\AssociatedSelector\Backend\Grid\ColumnSet'], ['Magento\ConfigurableProduct\Model\Resource\Product\Collection\AssociatedProduct'], ['Magento\ConfigurableProduct\Block\Adminhtml\Product\Edit\Tab\Super\Config\Grid\Renderer\Inventory'], @@ -3861,6 +3864,7 @@ ['Magento\Weee\Model\Observer\Session', 'Magento\Weee\Observer\*'], ['Magento\Tax\Model\Observer\Session', 'Magento\Tax\Observer\*'], ['Magento\Weee\Model\Observer', 'Magento\Weee\Observer\*'], + ['Magento\Tax\Observer\QuoteCollectTotalsBefore'], ['Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing\Validator\GroupPrice'], ['Magento\Catalog\Api\Data\ProductGroupPriceInterface'], ['Magento\Catalog\Api\ProductGroupPriceManagementInterface'],