/** * If module is enabled, don't run collect totals for shipping * * Tax calculation for shipping is handled in this class * @see \ClassyLlama\AvaTax\Model\Tax\Sales\Total\Quote\Tax::collect() * Since this extension doesn't support applying discounts *after* tax, we don't need to run a separate collect * process. * * @param \Magento\Tax\Model\Sales\Total\Quote\Shipping $subject * @param \Closure $proceed * @param \Magento\Quote\Model\Quote $quote * @param \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment * @param \Magento\Quote\Model\Quote\Address\Total $total * @return mixed */ public function aroundCollect(\Magento\Tax\Model\Sales\Total\Quote\Shipping $subject, \Closure $proceed, \Magento\Quote\Model\Quote $quote, \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment, \Magento\Quote\Model\Quote\Address\Total $total) { $storeId = $quote->getStoreId(); // If quote is virtual, getShipping will return billing address, so no need to check if quote is virtual $address = $shippingAssignment->getShipping()->getAddress(); if (!$this->config->isModuleEnabled($storeId) || $this->config->getTaxMode($storeId) == Config::TAX_MODE_NO_ESTIMATE_OR_SUBMIT || !$this->config->isAddressTaxable($address, $storeId)) { return $proceed($quote, $shippingAssignment, $total); } }
/** * @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 totals process. * * @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 $quote, \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment, \Magento\Quote\Model\Quote\Address\Total $total) { $this->_setAddress($shippingAssignment->getShipping()->getAddress()); $this->_setTotal($total); /** * Reset amounts */ $this->_setAmount(0); $this->_setBaseAmount(0); return $this; }
/** * Collect address discount amount * * @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(\Magento\Quote\Model\Quote $quote, \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment, \Magento\Quote\Model\Quote\Address\Total $total) { $address = $shippingAssignment->getShipping()->getAddress(); if ($this->_getDiscountCondition($address, $shippingAssignment)) { parent::collect($quote, $shippingAssignment, $total); $balance = $this->_getDiscountAmount(); $address->setDiscountCouponAmount($balance); $address->setBaseDiscountCouponAmount($balance); $total->setDiscountCouponDescription($this->getCode()); $total->setDiscountCouponAmount($balance); $total->setBaseDiscountCouponAmount($balance); } $total->addTotalAmount($this->getCode(), $address->getDiscountCouponAmount()); $total->addBaseTotalAmount($this->getCode(), $address->getBaseDiscountCouponAmount()); return $this; }
/** * Call tax calculation service to get tax details on the quote and items * * @param ShippingAssignmentInterface $shippingAssignment * @param Address\Total $total * @param bool $useBaseCurrency * @return \Magento\Tax\Api\Data\TaxDetailsInterface */ protected function getQuoteTaxDetails($shippingAssignment, $total, $useBaseCurrency) { $address = $shippingAssignment->getShipping()->getAddress(); //Setup taxable items $priceIncludesTax = $this->_config->priceIncludesTax($address->getQuote()->getStore()); $itemDataObjects = $this->mapItems($shippingAssignment, $priceIncludesTax, $useBaseCurrency); //Add shipping $shippingDataObject = $this->getShippingDataObject($shippingAssignment, $total, $useBaseCurrency); if ($shippingDataObject != null) { $itemDataObjects[] = $shippingDataObject; } //process extra taxable items associated only with quote $quoteExtraTaxables = $this->mapQuoteExtraTaxables($this->quoteDetailsItemDataObjectFactory, $address, $useBaseCurrency); if (!empty($quoteExtraTaxables)) { $itemDataObjects = array_merge($itemDataObjects, $quoteExtraTaxables); } //Preparation for calling taxCalculationService $quoteDetails = $this->prepareQuoteDetails($shippingAssignment, $itemDataObjects); $taxDetails = $this->taxCalculationService->calculateTax($quoteDetails, $address->getQuote()->getStore()->getStoreId()); return $taxDetails; }
/** * Update tax related fields for shipping * * @param ShippingAssignmentInterface $shippingAssignment * @param QuoteAddress\Total $total * @param TaxDetailsItemInterface $shippingTaxDetails * @param TaxDetailsItemInterface $baseShippingTaxDetails * @return $this */ 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()); $total->setShippingInclTax($shippingTaxDetails->getRowTotalInclTax()); $total->setBaseShippingInclTax($baseShippingTaxDetails->getRowTotalInclTax()); $total->setShippingTaxAmount($shippingTaxDetails->getRowTax()); $total->setBaseShippingTaxAmount($baseShippingTaxDetails->getRowTax()); //Add the shipping tax to total tax amount $total->addTotalAmount('tax', $shippingTaxDetails->getRowTax()); $total->addBaseTotalAmount('tax', $baseShippingTaxDetails->getRowTax()); if ($this->_config->discountTax($shippingAssignment->getShipping()->getAddress()->getQuote()->getStore())) { $total->setShippingAmountForDiscount($shippingTaxDetails->getRowTotalInclTax()); $total->setBaseShippingAmountForDiscount($baseShippingTaxDetails->getRowTotalInclTax()); } return $this; }
/** * Generate \Magento\Tax\Model\Sales\Quote\QuoteDetails object based on shipping assignment * * Base closely on this method, with the exception of the tax calculation call to calculate taxes: * @see \Magento\Tax\Model\Sales\Total\Quote\Tax::getQuoteTaxDetails() * * @param ShippingAssignmentInterface $shippingAssignment * @param Address\Total $total * @param bool $useBaseCurrency * @param string $storeId * @return \Magento\Tax\Api\Data\QuoteDetailsInterface */ protected function getTaxQuoteDetails($shippingAssignment, $total, $storeId, $useBaseCurrency) { // If quote is virtual, getShipping will return billing address, so no need to check if quote is virtual $address = $shippingAssignment->getShipping()->getAddress(); //Setup taxable items $priceIncludesTax = $this->_config->priceIncludesTax($address->getQuote()->getStore()); $itemDataObjects = $this->mapItems($shippingAssignment, $priceIncludesTax, $useBaseCurrency); //Add shipping $shippingDataObject = $this->getShippingDataObject($shippingAssignment, $total, $useBaseCurrency); if ($shippingDataObject != null) { $this->addInfoToQuoteDetailsItemForShipping($shippingDataObject, $storeId); $itemDataObjects[] = $shippingDataObject; } //process extra taxable items associated only with quote $quoteExtraTaxables = $this->mapQuoteExtraTaxables($this->quoteDetailsItemDataObjectFactory, $address, $useBaseCurrency); if (!empty($quoteExtraTaxables)) { $itemDataObjects = array_merge($itemDataObjects, $quoteExtraTaxables); } //Preparation for calling taxCalculationService $quoteDetails = $this->prepareQuoteDetails($shippingAssignment, $itemDataObjects); return $quoteDetails; }
/** * Convert Tax Quote Details into data to be converted to a GetTax Request * * @param \Magento\Tax\Api\Data\QuoteDetailsInterface $taxQuoteDetails * @param \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment * @param \Magento\Quote\Api\Data\CartInterface $quote * @return array|null */ protected function convertTaxQuoteDetailsToData(\Magento\Tax\Api\Data\QuoteDetailsInterface $taxQuoteDetails, \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment, \Magento\Quote\Api\Data\CartInterface $quote) { $lines = []; $items = $taxQuoteDetails->getItems(); $keyedItems = $this->taxCalculation->getKeyedItems($items); $childrenItems = $this->taxCalculation->getChildrenItems($items); /** @var \Magento\Tax\Api\Data\QuoteDetailsItemInterface $item */ foreach ($keyedItems as $item) { /** * If a quote has children and they are calculated (e.g., Bundled products with dynamic pricing) * @see \Magento\Tax\Model\Sales\Total\Quote\CommonTaxCollector::mapItems * then we only need to pass child items to AvaTax. Due to the logic in * @see \ClassyLlama\AvaTax\Framework\Interaction\TaxCalculation::calculateTaxDetails * the parent tax gets calculated based on children items */ // if (isset($childrenItems[$item->getCode()])) { /** @var \Magento\Tax\Api\Data\QuoteDetailsItemInterface $childItem */ foreach ($childrenItems[$item->getCode()] as $childItem) { $line = $this->interactionLine->getLine($childItem); if ($line) { $lines[] = $line; } } } else { $line = $this->interactionLine->getLine($item); if ($line) { $lines[] = $line; } } } // Shipping Address not documented in the interface for some reason // they do have a constant for it but not a method in the interface // // If quote is virtual, getShipping will return billing address, so no need to check if quote is virtual $shippingAddress = $shippingAssignment->getShipping()->getAddress(); $address = $this->address->getAddress($shippingAddress); $store = $this->storeRepository->getById($quote->getStoreId()); $currentDate = $this->getFormattedDate($store); // Quote created/updated date is not relevant, so just pass the current date $docDate = $currentDate; $customerUsageType = $quote->getCustomer() ? $this->taxClassHelper->getAvataxTaxCodeForCustomer($quote->getCustomer()) : null; return ['StoreId' => $store->getId(), 'Commit' => false, 'CurrencyCode' => $quote->getCurrency()->getQuoteCurrencyCode(), 'CustomerCode' => $this->getCustomerCode($quote), 'CustomerUsageType' => $customerUsageType, 'DestinationAddress' => $address, 'DocCode' => self::AVATAX_DOC_CODE_PREFIX . $quote->getId(), 'DocDate' => $docDate, 'DocType' => DocumentType::$SalesOrder, 'ExchangeRate' => $this->getExchangeRate($store, $quote->getCurrency()->getBaseCurrencyCode(), $quote->getCurrency()->getQuoteCurrencyCode()), 'ExchangeRateEffDate' => $currentDate, 'Lines' => $lines, 'DetailLevel' => DetailLevel::$Line, 'PurchaseOrderNo' => $quote->getReservedOrderId()]; }