/** * Collect tax totals for quote address * * @param \Magento\Quote\Model\Quote $quote * @param ShippingAssignmentInterface $shippingAssignment * @param Address\Total $total * @return $this * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ 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($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($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($shippingAssignment, $total, $shippingTaxDetails, $baseShippingTaxDetails); } //Process taxable items that are not product or shipping $this->processExtraTaxables($total, $itemsByType); //Save applied taxes for each item and the quote in aggregation $this->processAppliedTaxes($total, $shippingAssignment, $itemsByType); if ($this->includeExtraTax()) { $total->addTotalAmount('extra_tax', $total->getExtraTaxAmount()); $total->addBaseTotalAmount('extra_tax', $total->getBaseExtraTaxAmount()); } return $this; }
/** * @param ShippingAssignmentInterface $shippingAssignment * @param CartInterface $quote * @return void * @throws InputException */ public function save(CartInterface $quote, ShippingAssignmentInterface $shippingAssignment) { /** @var \Magento\Quote\Model\Quote $quote */ foreach ($shippingAssignment->getItems() as $item) { if (!$quote->getItemById($item->getItemId())) { $this->cartItemPersister->save($quote, $item); } } $this->shippingProcessor->save($shippingAssignment->getShipping(), $quote); }
/** * Collect tax totals for shipping. The result can be used to calculate discount on shipping * * @param \Magento\Quote\Model\Quote $quote * @param ShippingAssignmentInterface $shippingAssignment * @param Address\Total $total * @return $this */ 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($shippingAssignment, $total, false); $baseShippingDataObject = $this->getShippingDataObject($shippingAssignment, $total, true); if ($shippingDataObject == null || $baseShippingDataObject == null) { return $this; } $quoteDetails = $this->prepareQuoteDetails($shippingAssignment, [$shippingDataObject]); $taxDetails = $this->taxCalculationService->calculateTax($quoteDetails, $storeId); $baseQuoteDetails = $this->prepareQuoteDetails($shippingAssignment, [$baseShippingDataObject]); $baseTaxDetails = $this->taxCalculationService->calculateTax($baseQuoteDetails, $storeId); $this->processShippingTaxInfo($shippingAssignment, $total, $taxDetails->getItems()[self::ITEM_CODE_SHIPPING], $baseTaxDetails->getItems()[self::ITEM_CODE_SHIPPING]); return $this; }
/** * Calculate tax on product items. The result will be used to determine shipping * and discount later. * * @param \Magento\Quote\Model\Quote $quote * @param ShippingAssignmentInterface $shippingAssignment * @param Address\Total $total * @return $this */ 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; } $store = $quote->getStore(); $priceIncludesTax = $this->_config->priceIncludesTax($store); //Setup taxable items $itemDataObjects = $this->mapItems($shippingAssignment, $priceIncludesTax, false); $quoteDetails = $this->prepareQuoteDetails($shippingAssignment, $itemDataObjects); $taxDetails = $this->taxCalculationService->calculateTax($quoteDetails, $store->getStoreId()); $itemDataObjects = $this->mapItems($shippingAssignment, $priceIncludesTax, true); $baseQuoteDetails = $this->prepareQuoteDetails($shippingAssignment, $itemDataObjects); $baseTaxDetails = $this->taxCalculationService->calculateTax($baseQuoteDetails, $store->getStoreId()); $itemsByType = $this->organizeItemTaxDetailsByType($taxDetails, $baseTaxDetails); if (isset($itemsByType[self::ITEM_TYPE_PRODUCT])) { $this->processProductItems($shippingAssignment, $itemsByType[self::ITEM_TYPE_PRODUCT], $total); } return $this; }
/** * Process applied taxes for items and quote * * @param QuoteAddress\Total $total * @param array $itemsByType * @return $this */ protected function processAppliedTaxes(QuoteAddress\Total $total, ShippingAssignmentInterface $shippingAssignment, array $itemsByType) { $total->setAppliedTaxes([]); $allAppliedTaxesArray = []; /** @var AbstractItem[] $keyedAddressItems */ $keyedAddressItems = []; foreach ($shippingAssignment->getItems() as $addressItem) { $keyedAddressItems[$addressItem->getTaxCalculationItemId()] = $addressItem; } foreach ($itemsByType as $itemType => $items) { foreach ($items as $itemTaxCalculationId => $itemTaxDetails) { /** @var TaxDetailsItemInterface $taxDetails */ $taxDetails = $itemTaxDetails[self::KEY_ITEM]; $baseTaxDetails = $itemTaxDetails[self::KEY_BASE_ITEM]; $appliedTaxes = $taxDetails->getAppliedTaxes(); $baseAppliedTaxes = $baseTaxDetails->getAppliedTaxes(); $itemType = $taxDetails->getType(); $itemId = null; $associatedItemId = null; if ($itemType == self::ITEM_TYPE_PRODUCT) { //Use item id instead of tax calculation id $itemId = $keyedAddressItems[$itemTaxCalculationId]->getId(); } else { if ($taxDetails->getAssociatedItemCode() && $taxDetails->getAssociatedItemCode() != self::ASSOCIATION_ITEM_CODE_FOR_QUOTE) { //This item is associated with a product item $associatedItemId = $keyedAddressItems[$taxDetails->getAssociatedItemCode()]->getId(); } else { //This item is associated with an order, e.g., shipping, etc. $itemId = null; } } $extraInfo = ['item_id' => $itemId, 'item_type' => $itemType, 'associated_item_id' => $associatedItemId]; $appliedTaxesArray = $this->convertAppliedTaxes($appliedTaxes, $baseAppliedTaxes, $extraInfo); if ($itemType == self::ITEM_TYPE_PRODUCT) { $quoteItem = $keyedAddressItems[$itemTaxCalculationId]; $quoteItem->setAppliedTaxes($appliedTaxesArray); } $allAppliedTaxesArray[$itemTaxCalculationId] = $appliedTaxesArray; foreach ($appliedTaxesArray as $appliedTaxArray) { $this->_saveAppliedTaxes($total, [$appliedTaxArray], $appliedTaxArray['amount'], $appliedTaxArray['base_amount'], $appliedTaxArray['percent']); } } } $total->setItemsAppliedTaxes($allAppliedTaxesArray); return $this; }