/** * Check if tax amount should be included to grandtotal block * array( * $index => array( * 'amount' => $amount, * 'label' => $label, * 'font_size'=> $font_size * ) * ) * @return array */ public function getTotalsForDisplay() { $store = $this->getOrder()->getStore(); if ($this->_taxConfig->displaySalesTaxWithGrandTotal($store)) { return []; } $totals = []; if ($this->_taxConfig->displaySalesFullSummary($store)) { $totals = $this->getFullTaxInfo(); } $totals = array_merge($totals, parent::getTotalsForDisplay()); return $totals; }
/** * Check if tax amount should be included to grandtotals block * array( * $index => array( * 'amount' => $amount, * 'label' => $label, * 'font_size'=> $font_size * ) * ) * @return array */ public function getTotalsForDisplay() { $store = $this->getOrder()->getStore(); if (!$this->_taxConfig->displaySalesTaxWithGrandTotal($store)) { return parent::getTotalsForDisplay(); } $amount = $this->getOrder()->formatPriceTxt($this->getAmount()); $amountExclTax = $this->getAmount() - $this->getSource()->getTaxAmount(); $amountExclTax = $amountExclTax > 0 ? $amountExclTax : 0; $amountExclTax = $this->getOrder()->formatPriceTxt($amountExclTax); $tax = $this->getOrder()->formatPriceTxt($this->getSource()->getTaxAmount()); $fontSize = $this->getFontSize() ? $this->getFontSize() : 7; $totals = [['amount' => $this->getAmountPrefix() . $amountExclTax, 'label' => __('Grand Total (Excl. Tax)') . ':', 'font_size' => $fontSize]]; if ($this->_taxConfig->displaySalesFullSummary($store)) { $totals = array_merge($totals, $this->getFullTaxInfo()); } $totals[] = ['amount' => $this->getAmountPrefix() . $tax, 'label' => __('Tax') . ':', 'font_size' => $fontSize]; $totals[] = ['amount' => $this->getAmountPrefix() . $amount, 'label' => __('Grand Total (Incl. Tax)') . ':', 'font_size' => $fontSize]; return $totals; }
/** * @param array $addressData * @param bool $useBaseCurrency * @param string $shippingTaxClass * @param bool shippingPriceInclTax * @param array $expectedValue * @dataProvider getShippingDataObjectDataProvider */ public function testGetShippingDataObject(array $addressData, $useBaseCurrency, $shippingTaxClass, $shippingPriceInclTax) { $baseShippingAmount = $addressData['base_shipping_amount']; $shippingAmount = $addressData['shipping_amount']; $itemMock = $this->getMock('Magento\\Tax\\Api\\Data\\QuoteDetailsItemInterface'); $this->taxConfig->expects($this->any())->method('getShippingTaxClass')->with($this->store)->will($this->returnValue($shippingTaxClass)); $this->taxConfig->expects($this->any())->method('shippingPriceIncludesTax')->with($this->store)->will($this->returnValue($shippingPriceInclTax)); $this->address->expects($this->atLeastOnce())->method('getShippingDiscountAmount')->willReturn($shippingAmount); if ($shippingAmount) { if ($useBaseCurrency && $shippingAmount != 0) { $this->address->expects($this->once())->method('getBaseShippingDiscountAmount')->willReturn($baseShippingAmount); $this->quoteDetailsItemBuilderMock->expects($this->once())->method('setDiscountAmount')->with($baseShippingAmount); } else { $this->address->expects($this->never())->method('getBaseShippingDiscountAmount'); $this->quoteDetailsItemBuilderMock->expects($this->once())->method('setDiscountAmount')->with($shippingAmount); } } foreach ($addressData as $key => $value) { $this->address->setData($key, $value); } $this->taxClassKeyBuilderMock->expects($this->any())->method('setType')->willReturnSelf(); $this->taxClassKeyBuilderMock->expects($this->any())->method('setValue')->with($shippingTaxClass)->willReturnSelf(); $this->quoteDetailsItemBuilderMock->expects($this->once())->method('create')->willReturn($itemMock); $this->assertEquals($itemMock, $this->commonTaxCollector->getShippingDataObject($this->address, $useBaseCurrency)); }
/** * Check if we need display both sobtotals * * @return bool */ public function displayBoth() { /** * Check without store parameter - we wil get admin configuration value */ return $this->_taxConfig->displayCartSubtotalBoth(); }
/** * @param \Magento\Quote\Model\Cart\TotalsConverter $subject * @param \Closure $proceed * @param \Magento\Quote\Model\Quote\Address\Total[] $addressTotals * @return \Magento\Quote\Api\Data\TotalSegmentInterface[] * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function aroundProcess(\Magento\Quote\Model\Cart\TotalsConverter $subject, \Closure $proceed, array $addressTotals = []) { $totalSegments = $proceed($addressTotals); if (!array_key_exists($this->code, $addressTotals)) { return $totalSegments; } $taxes = $addressTotals['tax']->getData(); if (!array_key_exists('full_info', $taxes)) { return $totalSegments; } $detailsId = 1; $finalData = []; foreach (unserialize($taxes['full_info']) as $info) { if (array_key_exists('hidden', $info) && $info['hidden'] || $info['amount'] == 0 && $this->taxConfig->displayCartZeroTax()) { continue; } $taxDetails = $this->detailsFactory->create([]); $taxDetails->setAmount($info['amount']); $taxRates = $this->getRatesData($info['rates']); $taxDetails->setRates($taxRates); $taxDetails->setGroupId($detailsId); $finalData[] = $taxDetails; $detailsId++; } $attributes = $totalSegments[$this->code]->getExtensionAttributes(); if ($attributes === null) { $attributes = $this->totalSegmentExtensionFactory->create(); } $attributes->setTaxGrandtotalDetails($finalData); $totalSegments[$this->code]->setExtensionAttributes($attributes); return $totalSegments; }
/** * Check if we have include tax amount between grandtotal incl/excl tax * * @return bool */ public function includeTax() { if ($this->getTotal()->getValue()) { return $this->_taxConfig->displayCartTaxWithGrandTotal($this->getStore()); } return false; }
/** * Tests the getCalculationSequence method * * @param bool $applyTaxAfterDiscount * @param bool $discountTaxIncl * @param string $expectedValue * @dataProvider dataProviderGetCalculationSequence */ public function testGetCalculationSequence($applyTaxAfterDiscount, $discountTaxIncl, $expectedValue) { $scopeConfigMock = $this->getMockForAbstractClass('Magento\\Framework\\App\\Config\\ScopeConfigInterface'); $scopeConfigMock->expects($this->at(0))->method('getValue')->will($this->returnValue($applyTaxAfterDiscount)); $scopeConfigMock->expects($this->at(1))->method('getValue')->will($this->returnValue($discountTaxIncl)); /** @var \Magento\Tax\Model\Config */ $model = new Config($scopeConfigMock); $this->assertEquals($expectedValue, $model->getCalculationSequence()); }
/** * @param array $addressData * @param bool $useBaseCurrency * @param string $shippingTaxClass * @param bool shippingPriceInclTax * @param array $expectedValue * @dataProvider getShippingDataObjectDataProvider */ public function testGetShippingDataObject(array $addressData, $useBaseCurrency, $shippingTaxClass, $shippingPriceInclTax, array $expectedValue) { $this->taxConfig->expects($this->any())->method('getShippingTaxClass')->with($this->store)->will($this->returnValue($shippingTaxClass)); $this->taxConfig->expects($this->any())->method('shippingPriceIncludesTax')->with($this->store)->will($this->returnValue($shippingPriceInclTax)); foreach ($addressData as $key => $value) { $this->address->setData($key, $value); } $shippingDataObject = $this->commonTaxCollector->getShippingDataObject($this->address, $useBaseCurrency); $this->assertEquals($expectedValue, $shippingDataObject->__toArray()); //call it again, make sure we get the same output $shippingDataObject = $this->commonTaxCollector->getShippingDataObject($this->address, $useBaseCurrency); $this->assertEquals($expectedValue, $shippingDataObject->__toArray()); }
/** * @param array $addressData * @param bool $useBaseCurrency * @param string $shippingTaxClass * @param bool $shippingPriceInclTax * @dataProvider getShippingDataObjectDataProvider */ public function testGetShippingDataObject(array $addressData, $useBaseCurrency, $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)->will($this->returnValue($shippingTaxClass)); $this->taxConfig->expects($this->any())->method('shippingPriceIncludesTax')->with($this->store)->will($this->returnValue($shippingPriceInclTax)); $totalsMock->expects($this->atLeastOnce())->method('getShippingDiscountAmount')->willReturn($shippingAmount); if ($shippingAmount) { if ($useBaseCurrency && $shippingAmount != 0) { $totalsMock->expects($this->once())->method('getBaseShippingDiscountAmount')->willReturn($baseShippingAmount); $expectedDiscountAmount = $baseShippingAmount; } else { $totalsMock->expects($this->never())->method('getBaseShippingDiscountAmount'); $expectedDiscountAmount = $shippingAmount; } } foreach ($addressData as $key => $value) { $totalsMock->setData($key, $value); } $this->assertEquals($this->quoteDetailsItemDataObject, $this->commonTaxCollector->getShippingDataObject($shippingAssignmentMock, $totalsMock, $useBaseCurrency)); if ($shippingAmount) { $this->assertEquals($expectedDiscountAmount, $this->quoteDetailsItemDataObject->getDiscountAmount()); } }
/** * Add tax totals information to address object * * @param Address $address * @return $this * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function fetch(Address $address) { $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; $area = null; if ($this->_config->displayCartTaxWithGrandTotal($store) && $address->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]); } $store = $address->getQuote()->getStore(); /** * Modify subtotal */ if ($this->_config->displayCartSubtotalBoth($store) || $this->_config->displayCartSubtotalInclTax($store)) { if ($address->getSubtotalInclTax() > 0) { $subtotalInclTax = $address->getSubtotalInclTax(); } else { $subtotalInclTax = $address->getSubtotal() + $taxAmount - $address->getShippingTaxAmount(); } $address->addTotal(['code' => 'subtotal', 'title' => __('Subtotal'), 'value' => $subtotalInclTax, 'value_incl_tax' => $subtotalInclTax, 'value_excl_tax' => $address->getSubtotal()]); } return $this; }
/** * Add tax totals information to address object * * @param \Magento\Quote\Model\Quote $quote * @param Address\Total $total * @return array|null * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ public function fetch(\Magento\Quote\Model\Quote $quote, \Magento\Quote\Model\Quote\Address\Total $total) { $totals = []; $store = $quote->getStore(); $applied = $total->getAppliedTaxes(); if (is_string($applied)) { $applied = unserialize($applied); } $amount = $total->getTaxAmount(); if ($amount == null) { $this->enhanceTotalData($quote, $total); $amount = $total->getTaxAmount(); } $taxAmount = $amount + $total->getTotalAmount('discount_tax_compensation'); $area = null; if ($this->_config->displayCartTaxWithGrandTotal($store) && $total->getGrandTotal()) { $area = 'taxes'; } $totals[] = ['code' => $this->getCode(), 'title' => __('Tax'), 'full_info' => $applied ? $applied : [], 'value' => $amount, 'area' => $area]; /** * Modify subtotal */ if ($this->_config->displayCartSubtotalBoth($store) || $this->_config->displayCartSubtotalInclTax($store)) { if ($total->getSubtotalInclTax() > 0) { $subtotalInclTax = $total->getSubtotalInclTax(); } else { $subtotalInclTax = $total->getSubtotal() + $taxAmount - $total->getShippingTaxAmount(); } $totals[] = ['code' => 'subtotal', 'title' => __('Subtotal'), 'value' => $subtotalInclTax, 'value_incl_tax' => $subtotalInclTax, 'value_excl_tax' => $total->getSubtotal()]; } if (empty($totals)) { return null; } return $totals; }
/** * @param CartTotalRepository $subject * @param \Closure $proceed * @param int $cartId * @return \Magento\Quote\Model\Cart\Totals * @throws \Magento\Framework\Exception\NoSuchEntityException * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function aroundGet(CartTotalRepository $subject, \Closure $proceed, $cartId) { $result = $proceed($cartId); $quote = $this->quoteRepository->getActive($cartId); $totals = $quote->getTotals(); if (!array_key_exists('tax', $totals)) { return $result; } $taxes = $totals['tax']->getData(); if (!array_key_exists('full_info', $taxes)) { return $result; } $detailsId = 1; $finalData = []; foreach ($taxes['full_info'] as $info) { if (array_key_exists('hidden', $info) && $info['hidden'] || $info['amount'] == 0 && $this->taxConfig->displayCartZeroTax()) { continue; } $taxDetails = $this->detailsFactory->create([]); $taxDetails->setAmount($info['amount']); $taxRates = $this->getRatesData($info['rates']); $taxDetails->setRates($taxRates); $taxDetails->setGroupId($detailsId); $finalData[] = $taxDetails; $detailsId++; } $attributes = $result->getExtensionAttributes(); if ($attributes === null) { $attributes = $this->extensionFactory->create(); } $attributes->setTaxGrandtotalDetails($finalData); /** @var $result \Magento\Quote\Model\Cart\Totals */ $result->setExtensionAttributes($attributes); return $result; }
/** * Build message text * Determine which notification and data to display * * @return string */ public function getText() { $messageDetails = ''; if (!empty($this->storesWithInvalidDisplaySettings) && !$this->taxConfig->isWrongDisplaySettingsIgnored()) { $messageDetails .= '<strong>'; $messageDetails .= __('Warning tax configuration can result in rounding errors. '); $messageDetails .= '</strong><br>'; $messageDetails .= __('Store(s) affected: '); $messageDetails .= implode(', ', $this->storesWithInvalidDisplaySettings); $messageDetails .= '<br><div style="text-align:right">'; $messageDetails .= __('Click on the link to <a href="%1">ignore this notification</a>', $this->getIgnoreTaxNotificationUrl('price_display')); $messageDetails .= "</div><br>"; } if (!empty($this->storesWithInvalidDiscountSettings) && !$this->taxConfig->isWrongDiscountSettingsIgnored()) { $messageDetails .= '<strong>'; $messageDetails .= __('Warning tax discount configuration might result in different discounts than a customer might expect. '); $messageDetails .= '</strong><br>'; $messageDetails .= __('Store(s) affected: '); $messageDetails .= implode(', ', $this->storesWithInvalidDiscountSettings); $messageDetails .= '<br><div style="text-align:right">'; $messageDetails .= __('Click on the link to <a href="%1">ignore this notification</a>', $this->getIgnoreTaxNotificationUrl('discount')); $messageDetails .= "</div><br>"; } $messageDetails .= '<br>'; $messageDetails .= __('Please see <a href="%1">documentation</a> for more details. ', $this->getInfoUrl()); $messageDetails .= __('Click here to go to <a href="%1">Tax Configuration</a> and change your settings.', $this->getManageUrl()); return $messageDetails; }
/** * Check if tax rate is same as store tax rate * * @param float $rate * @param float $storeRate * @return bool */ protected function isSameRateAsStore($rate, $storeRate) { if ((bool) $this->config->crossBorderTradeEnabled($this->storeId)) { return true; } else { return abs($rate - $storeRate) < 1.0E-5; } }
/** * Get array of arrays with totals information for display in PDF * array( * $index => array( * 'amount' => $amount, * 'label' => $label, * 'font_size'=> $font_size * ) * ) * @return array */ public function getTotalsForDisplay() { $store = $this->getOrder()->getStore(); $amount = $this->getOrder()->formatPriceTxt($this->getAmount()); $amountInclTax = $this->getSource()->getShippingInclTax(); if (!$amountInclTax) { $amountInclTax = $this->getAmount() + $this->getSource()->getShippingTaxAmount(); } $amountInclTax = $this->getOrder()->formatPriceTxt($amountInclTax); $fontSize = $this->getFontSize() ? $this->getFontSize() : 7; if ($this->_taxConfig->displaySalesShippingBoth($store)) { $totals = [['amount' => $this->getAmountPrefix() . $amount, 'label' => __('Shipping (Excl. Tax)') . ':', 'font_size' => $fontSize], ['amount' => $this->getAmountPrefix() . $amountInclTax, 'label' => __('Shipping (Incl. Tax)') . ':', 'font_size' => $fontSize]]; } elseif ($this->_taxConfig->displaySalesShippingInclTax($store)) { $totals = [['amount' => $this->getAmountPrefix() . $amountInclTax, 'label' => __($this->getTitle()) . ':', 'font_size' => $fontSize]]; } else { $totals = [['amount' => $this->getAmountPrefix() . $amount, 'label' => __($this->getTitle()) . ':', 'font_size' => $fontSize]]; } return $totals; }
/** * Get label for shipping total based on configuration settings * * @return string */ public function getShippingLabel() { $source = $this->getSource(); if ($this->_taxConfig->displaySalesShippingInclTax($source->getOrder()->getStoreId())) { $label = __('Refund Shipping (Incl. Tax)'); } elseif ($this->_taxConfig->displaySalesShippingBoth($source->getOrder()->getStoreId())) { $label = __('Refund Shipping (Excl. Tax)'); } else { $label = __('Refund Shipping'); } return $label; }
/** * @param array $addressData * @param bool $useBaseCurrency * @param string $shippingTaxClass * @param bool $shippingPriceInclTax * @dataProvider getShippingDataObjectDataProvider */ public function testGetShippingDataObject(array $addressData, $useBaseCurrency, $shippingTaxClass, $shippingPriceInclTax) { $baseShippingAmount = $addressData['base_shipping_amount']; $shippingAmount = $addressData['shipping_amount']; $this->taxConfig->expects($this->any())->method('getShippingTaxClass')->with($this->store)->will($this->returnValue($shippingTaxClass)); $this->taxConfig->expects($this->any())->method('shippingPriceIncludesTax')->with($this->store)->will($this->returnValue($shippingPriceInclTax)); $this->address->expects($this->atLeastOnce())->method('getShippingDiscountAmount')->willReturn($shippingAmount); if ($shippingAmount) { if ($useBaseCurrency && $shippingAmount != 0) { $this->address->expects($this->once())->method('getBaseShippingDiscountAmount')->willReturn($baseShippingAmount); $expectedDiscountAmount = $baseShippingAmount; } else { $this->address->expects($this->never())->method('getBaseShippingDiscountAmount'); $expectedDiscountAmount = $shippingAmount; } } foreach ($addressData as $key => $value) { $this->address->setData($key, $value); } $this->assertEquals($this->quoteDetailsItemDataObject, $this->commonTaxCollector->getShippingDataObject($this->address, $useBaseCurrency)); if ($shippingAmount) { $this->assertEquals($expectedDiscountAmount, $this->quoteDetailsItemDataObject->getDiscountAmount()); } }
/** * Update tax related fields for shipping * * @param QuoteAddress $address * @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_hidden_tax', $shippingTaxDetails->getDiscountTaxCompensationAmount()); $address->setBaseTotalAmount('shipping_hidden_tax', $baseShippingTaxDetails->getDiscountTaxCompensationAmount()); $address->setShippingInclTax($shippingTaxDetails->getRowTotalInclTax()); $address->setBaseShippingInclTax($baseShippingTaxDetails->getRowTotalInclTax()); $address->setShippingTaxAmount($shippingTaxDetails->getRowTax()); $address->setBaseShippingTaxAmount($baseShippingTaxDetails->getRowTax()); //Add the shipping tax to total tax amount $address->addTotalAmount('tax', $shippingTaxDetails->getRowTax()); $address->addBaseTotalAmount('tax', $baseShippingTaxDetails->getRowTax()); if ($this->_config->discountTax($address->getQuote()->getStore())) { $address->setShippingAmountForDiscount($shippingTaxDetails->getRowTotalInclTax()); $address->setBaseShippingAmountForDiscount($baseShippingTaxDetails->getRowTotalInclTax()); } return $this; }
/** * situation: The admin user specified the desired refund amount that has taxes embedded within it * * @throws \Magento\Framework\Exception\LocalizedException */ public function testCollectUsingTaxInclShippingAmount() { $this->taxConfig->expects($this->any())->method('displaySalesShippingInclTax')->willReturn(true); $orderShippingAmount = 15; $shippingTaxAmount = 3; $orderShippingInclTax = $orderShippingAmount + $shippingTaxAmount; $orderShippingAmountRefunded = 5; $shippingTaxRefunded = 1; $refundedInclTax = $orderShippingAmountRefunded + $shippingTaxRefunded; $currencyMultiple = 2; $baseOrderShippingAmount = $orderShippingAmount * $currencyMultiple; $baseShippingTaxAmount = $shippingTaxAmount * $currencyMultiple; $baseOrderShippingInclTax = $orderShippingInclTax * $currencyMultiple; $baseOrderShippingAmountRefunded = $orderShippingAmountRefunded * $currencyMultiple; $baseShippingTaxRefunded = $shippingTaxRefunded * $currencyMultiple; $baseRefundedInclTax = $refundedInclTax * $currencyMultiple; //determine expected amounts $desiredRefundAmount = $baseOrderShippingInclTax - $baseRefundedInclTax; $expectedShippingAmount = $orderShippingAmount - $orderShippingAmountRefunded; $expectedShippingAmountInclTax = $orderShippingInclTax - $refundedInclTax; $expectedBaseShippingAmount = $expectedShippingAmount * $currencyMultiple; $expectedBaseShippingAmountInclTax = $expectedShippingAmountInclTax * $currencyMultiple; $grandTotalBefore = 100; $baseGrandTotalBefore = 200; $expectedGrandTotal = $grandTotalBefore + $expectedShippingAmount; $expectedBaseGrandTtoal = $baseGrandTotalBefore + $expectedBaseShippingAmount; $order = new \Magento\Framework\DataObject(['shipping_amount' => $orderShippingAmount, 'base_shipping_amount' => $baseOrderShippingAmount, 'shipping_refunded' => $orderShippingAmountRefunded, 'base_shipping_refunded' => $baseOrderShippingAmountRefunded, 'shipping_incl_tax' => $orderShippingInclTax, 'base_shipping_incl_tax' => $baseOrderShippingInclTax, 'shipping_tax_amount' => $shippingTaxAmount, 'shipping_tax_refunded' => $shippingTaxRefunded, 'base_shipping_tax_amount' => $baseShippingTaxAmount, 'base_shipping_tax_refunded' => $baseShippingTaxRefunded]); $this->creditmemoMock->expects($this->once())->method('getOrder')->willReturn($order); $this->creditmemoMock->expects($this->once())->method('hasBaseShippingAmount')->willReturn(true); $this->creditmemoMock->expects($this->once())->method('getBaseShippingAmount')->willReturn($desiredRefundAmount); $this->creditmemoMock->expects($this->once())->method('getGrandTotal')->willReturn($grandTotalBefore); $this->creditmemoMock->expects($this->once())->method('getBaseGrandTotal')->willReturn($baseGrandTotalBefore); //verify $this->creditmemoMock->expects($this->once())->method('setShippingAmount')->with($expectedShippingAmount)->willReturnSelf(); $this->creditmemoMock->expects($this->once())->method('setBaseShippingAmount')->with($expectedBaseShippingAmount)->willReturnSelf(); $this->creditmemoMock->expects($this->once())->method('setShippingInclTax')->with($expectedShippingAmountInclTax)->willReturnSelf(); $this->creditmemoMock->expects($this->once())->method('setBaseShippingInclTax')->with($expectedBaseShippingAmountInclTax)->willReturnSelf(); $this->creditmemoMock->expects($this->once())->method('setGrandTotal')->with($expectedGrandTotal)->willReturnSelf(); $this->creditmemoMock->expects($this->once())->method('setBaseGrandTotal')->with($expectedBaseGrandTtoal)->willReturnSelf(); $this->shippingCollector->collect($this->creditmemoMock); }
/** * {@inheritdoc} */ public function calculateTax(\Magento\Tax\Api\Data\QuoteDetailsInterface $quoteDetails, $storeId = null, $round = true) { if ($storeId === null) { $storeId = $this->storeManager->getStore()->getStoreId(); } // initial TaxDetails data $taxDetailsData = [TaxDetailsInterface::KEY_SUBTOTAL => 0.0, TaxDetailsInterface::KEY_TAX_AMOUNT => 0.0, TaxDetailsInterface::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT => 0.0, TaxDetailsInterface::KEY_APPLIED_TAXES => [], TaxDetailsInterface::KEY_ITEMS => []]; $items = $quoteDetails->getItems(); if (empty($items)) { return $this->taxDetailsDataObjectFactory->create()->setSubtotal(0.0)->setTaxAmount(0.0)->setDiscountTaxCompensationAmount(0.0)->setAppliedTaxes([])->setItems([]); } $this->computeRelationships($items); $calculator = $this->calculatorFactory->create($this->config->getAlgorithm($storeId), $storeId, $quoteDetails->getBillingAddress(), $quoteDetails->getShippingAddress(), $this->taxClassManagement->getTaxClassId($quoteDetails->getCustomerTaxClassKey(), 'customer'), $quoteDetails->getCustomerId()); $processedItems = []; /** @var QuoteDetailsItemInterface $item */ foreach ($this->keyedItems as $item) { if (isset($this->parentToChildren[$item->getCode()])) { $processedChildren = []; foreach ($this->parentToChildren[$item->getCode()] as $child) { $processedItem = $this->processItem($child, $calculator, $round); $taxDetailsData = $this->aggregateItemData($taxDetailsData, $processedItem); $processedItems[$processedItem->getCode()] = $processedItem; $processedChildren[] = $processedItem; } $processedItem = $this->calculateParent($processedChildren, $item->getQuantity()); $processedItem->setCode($item->getCode()); $processedItem->setType($item->getType()); } else { $processedItem = $this->processItem($item, $calculator, $round); $taxDetailsData = $this->aggregateItemData($taxDetailsData, $processedItem); } $processedItems[$processedItem->getCode()] = $processedItem; } $taxDetailsDataObject = $this->taxDetailsDataObjectFactory->create(); $this->dataObjectHelper->populateWithArray($taxDetailsDataObject, $taxDetailsData, '\\Magento\\Tax\\Api\\Data\\TaxDetailsInterface'); $taxDetailsDataObject->setItems($processedItems); return $taxDetailsDataObject; }
/** * @return $this */ protected function _initGrandTotal() { $store = $this->getStore(); $parent = $this->getParentBlock(); $grandototal = $parent->getTotal('grand_total'); if (!$grandototal || !(double) $this->_source->getGrandTotal()) { return $this; } if ($this->_config->displaySalesTaxWithGrandTotal($store)) { $grandtotal = $this->_source->getGrandTotal(); $baseGrandtotal = $this->_source->getBaseGrandTotal(); $grandtotalExcl = $grandtotal - $this->_source->getTaxAmount(); $baseGrandtotalExcl = $baseGrandtotal - $this->_source->getBaseTaxAmount(); $grandtotalExcl = max($grandtotalExcl, 0); $baseGrandtotalExcl = max($baseGrandtotalExcl, 0); $totalExcl = new \Magento\Framework\DataObject(['code' => 'grand_total', 'strong' => true, 'value' => $grandtotalExcl, 'base_value' => $baseGrandtotalExcl, 'label' => __('Grand Total (Excl.Tax)')]); $totalIncl = new \Magento\Framework\DataObject(['code' => 'grand_total_incl', 'strong' => true, 'value' => $grandtotal, 'base_value' => $baseGrandtotal, 'label' => __('Grand Total (Incl.Tax)')]); $parent->addTotal($totalExcl, 'grand_total'); $this->_addTax('grand_total'); $parent->addTotal($totalIncl, 'tax'); } return $this; }
/** * {@inheritdoc} */ public function calculateTax(QuoteDetails $quoteDetails, $storeId = null) { if (is_null($storeId)) { $storeId = $this->storeManager->getStore()->getStoreId(); } // initial TaxDetails data $taxDetailsData = [TaxDetails::KEY_SUBTOTAL => 0.0, TaxDetails::KEY_TAX_AMOUNT => 0.0, TaxDetails::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT => 0.0, TaxDetails::KEY_APPLIED_TAXES => [], TaxDetails::KEY_ITEMS => []]; $items = $quoteDetails->getItems(); if (empty($items)) { return $this->taxDetailsBuilder->populateWithArray($taxDetailsData)->create(); } $this->computeRelationships($items); $calculator = $this->calculatorFactory->create($this->config->getAlgorithm($storeId), $storeId, $quoteDetails->getBillingAddress(), $quoteDetails->getShippingAddress(), $this->taxClassService->getTaxClassId($quoteDetails->getCustomerTaxClassKey(), 'customer'), $quoteDetails->getCustomerId()); $processedItems = []; /** @var QuoteDetailsItem $item */ foreach ($this->keyedItems as $item) { if (isset($this->parentToChildren[$item->getCode()])) { $processedChildren = []; foreach ($this->parentToChildren[$item->getCode()] as $child) { $processedItem = $this->processItem($child, $calculator); $taxDetailsData = $this->aggregateItemData($taxDetailsData, $processedItem); $processedItems[$processedItem->getCode()] = $processedItem; $processedChildren[] = $processedItem; } $processedItemBuilder = $this->calculateParent($processedChildren, $item->getQuantity()); $processedItemBuilder->setCode($item->getCode()); $processedItemBuilder->setType($item->getType()); $processedItem = $processedItemBuilder->create(); } else { $processedItem = $this->processItem($item, $calculator); $taxDetailsData = $this->aggregateItemData($taxDetailsData, $processedItem); } $processedItems[$processedItem->getCode()] = $processedItem; } return $this->taxDetailsBuilder->populateWithArray($taxDetailsData)->setItems($processedItems)->create(); }
public function testGetDisplaySubtotalBoth() { $this->taxConfig->expects($this->once())->method('displayCartSubtotalBoth'); $this->totalsObj->getDisplaySubtotalBoth(); }
/** * Check if we need display grid totals include tax * * @return bool */ public function displayTotalsIncludeTax() { $result = $this->_taxConfig->displayCartSubtotalInclTax($this->getStore()) || $this->_taxConfig->displayCartSubtotalBoth($this->getStore()); return $result; }
/** * @return bool */ public function displayBoth() { return $this->_taxConfig->displayCartSubtotalBoth($this->getStore()); }
/** * Calculate item price and row total including/excluding tax based on total price rounding level * * @param QuoteDetailsItem $item * @param \Magento\Framework\Object $taxRateRequest * @param int $storeId * @return TaxDetailsItem */ protected function totalBaseCalculation(QuoteDetailsItem $item, \Magento\Framework\Object $taxRateRequest, $storeId) { /** @var \Magento\Tax\Service\V1\Data\TaxDetails\AppliedTax[] $appliedTaxes */ $appliedTaxes = []; $appliedTaxBuilder = $this->taxDetailsItemBuilder->getAppliedTaxBuilder(); $appliedTaxRateBuilder = $appliedTaxBuilder->getAppliedTaxRateBuilder(); $taxRateRequest->setProductClassId($item->getTaxClassId()); $appliedRates = $this->calculator->getAppliedRates($taxRateRequest); $rate = $this->calculator->getRate($taxRateRequest); $quantity = $this->getTotalQuantity($item); $price = $priceInclTax = $this->calculator->round($item->getUnitPrice()); $rowTotal = $rowTotalInclTax = $taxableAmount = $this->calcRowTotal($item); $discountAmount = $item->getDiscountAmount(); $applyTaxAfterDiscount = $this->config->applyTaxAfterDiscount($storeId); $discountTaxCompensationAmount = 0; $isTotalBasedCalculation = $this->config->getAlgorithm($storeId) == Calculation::CALC_TOTAL_BASE; if ($item->getTaxIncluded()) { if ($taxRateRequest->getSameRateAsStore()) { $rowTaxExact = $this->calculator->calcTaxAmount($rowTotalInclTax, $rate, true, false); if ($isTotalBasedCalculation) { $rowTax = $this->deltaRound($rowTaxExact, $rate, true); } else { $rowTax = $this->calculator->round($rowTaxExact); } $rowTotal = $rowTotalInclTax - $rowTax; $price = $this->calculator->round($rowTotal / $quantity); } else { $storeRate = $this->calculator->getStoreRate($taxRateRequest, $storeId); $priceInclTax = $this->calculatePriceInclTax($price, $storeRate, $rate); $rowTotalInclTax = $priceInclTax * $quantity; $taxableAmount = $rowTotalInclTax; if ($isTotalBasedCalculation) { $rowTax = $this->deltaRound($this->calculator->calcTaxAmount($rowTotalInclTax, $rate, true, false), $rate, true); } else { $rowTax = $this->calculator->calcTaxAmount($rowTotalInclTax, $rate, true, true); } $rowTotal = $rowTotalInclTax - $rowTax; $price = $this->calculator->round($rowTotal / $quantity); } //Handle discount if ($discountAmount && $applyTaxAfterDiscount) { //TODO: handle originalDiscountAmount $taxableAmount = max($taxableAmount - $discountAmount, 0); $rowTaxAfterDiscount = $this->calculator->calcTaxAmount($taxableAmount, $rate, true, false); if ($isTotalBasedCalculation) { //Round the row tax using a different type so that we don't pollute the rounding deltas $rowTaxAfterDiscount = $this->deltaRound($rowTaxAfterDiscount, $rate, true, self::KEY_TAX_AFTER_DISCOUNT_DELTA_ROUNDING); } else { $rowTaxAfterDiscount = $this->calculator->round($rowTaxAfterDiscount); } // Set discount tax compensation $discountTaxCompensationAmount = $rowTax - $rowTaxAfterDiscount; $rowTax = $rowTaxAfterDiscount; } //save applied taxes $appliedTaxes = $this->getAppliedTaxes($appliedTaxBuilder, $rowTax, $rate, $appliedRates); } else { //catalog price does not include tax $appliedRates = $this->calculator->getAppliedRates($taxRateRequest); $rowTaxes = []; $rowTaxesBeforeDiscount = []; //Apply each tax rate separately foreach ($appliedRates as $appliedRate) { $taxId = $appliedRate['id']; $taxRate = $appliedRate['percent']; if ($isTotalBasedCalculation) { $rowTaxPerRate = $this->deltaRound($this->calculator->calcTaxAmount($rowTotal, $taxRate, false, false), $taxId, false); } else { $rowTaxPerRate = $this->calculator->calcTaxAmount($rowTotal, $taxRate, false, true); } $rowTaxAfterDiscount = $rowTaxPerRate; //Handle discount if ($discountAmount && $applyTaxAfterDiscount) { //TODO: handle originalDiscountAmount $rowTaxAfterDiscount = $this->calculator->calcTaxAmount(max($rowTotal - $discountAmount, 0), $taxRate, false, false); if ($isTotalBasedCalculation) { //Round the row tax using a different type so that we don't pollute the rounding deltas $rowTaxAfterDiscount = $this->deltaRound($rowTaxAfterDiscount, $taxRate, false, self::KEY_TAX_AFTER_DISCOUNT_DELTA_ROUNDING); } else { $rowTaxAfterDiscount = $this->calculator->round($rowTaxAfterDiscount); } } $appliedTaxes[$appliedRate['id']] = $this->getAppliedTax($appliedTaxBuilder, $rowTaxAfterDiscount, $appliedRate); $rowTaxes[] = $rowTaxAfterDiscount; $rowTaxesBeforeDiscount[] = $rowTaxPerRate; } $rowTax = array_sum($rowTaxes); $rowTaxBeforeDiscount = array_sum($rowTaxesBeforeDiscount); $rowTotalInclTax = $rowTotal + $rowTaxBeforeDiscount; $priceInclTax = $this->calculator->round($rowTotalInclTax / $quantity); } $this->taxDetailsItemBuilder->setCode($item->getCode()); $this->taxDetailsItemBuilder->setRowTax($rowTax); $this->taxDetailsItemBuilder->setPrice($price); $this->taxDetailsItemBuilder->setPriceInclTax($priceInclTax); $this->taxDetailsItemBuilder->setRowTotal($rowTotal); $this->taxDetailsItemBuilder->setRowTotalInclTax($rowTotalInclTax); $this->taxDetailsItemBuilder->setCode($item->getCode()); $this->taxDetailsItemBuilder->setType($item->getType()); $this->taxDetailsItemBuilder->setTaxPercent($rate); $this->taxDetailsItemBuilder->setDiscountTaxCompensationAmount($discountTaxCompensationAmount); $this->taxDetailsItemBuilder->setAppliedTaxes($appliedTaxes); return $this->taxDetailsItemBuilder->create(); }
/** * Get totals config * * @return array */ protected function getTotalsConfig() { return ['display_subtotal_incl_tax' => (int) $this->taxConfig->displayCartSubtotalInclTax(), 'display_cart_subtotal_excl_tax' => (int) $this->taxConfig->displayCartSubtotalExclTax()]; }
/** * Display tax in grand total section or not * * @return bool */ public function isTaxDisplayedInGrandTotal() { return $this->taxConfig->displayCartTaxWithGrandTotal(); }
/** * Return whether cross border trade is enabled or not * * @param null|int|string|Store $store * @return bool */ public function isCrossBorderTradeEnabled($store = null) { return (bool) $this->_config->crossBorderTradeEnabled($store); }
/** * Check if we need display shipping include tax * * @return bool */ public function displayIncludeTax() { return $this->_taxConfig->displayCartShippingInclTax($this->getStore()); }