/** * Cache validated response * * @param ValidateRequest $validateRequest * @param $storeId * @return ValidateResult * @throws \SoapFault */ public function validate(ValidateRequest $validateRequest, $storeId) { $addressCacheKey = $this->getCacheKey($validateRequest->getAddress()) . $storeId; $validateResult = @unserialize($this->cache->load($addressCacheKey)); if ($validateResult instanceof ValidateResult) { $this->avaTaxLogger->addDebug('Loaded \\AvaTax\\ValidateResult from cache.', ['request' => var_export($validateRequest, true), 'result' => var_export($validateResult, true), 'cache_key' => $addressCacheKey]); return $validateResult; } try { $addressService = $this->interactionAddress->getAddressService($this->type, $storeId); $validateResult = $addressService->validate($validateRequest); $serializedValidateResult = serialize($validateResult); $this->cache->save($serializedValidateResult, $addressCacheKey, [Config::AVATAX_CACHE_TAG]); $validAddress = isset($validateResult->getValidAddresses()[0]) ? $validateResult->getValidAddresses()[0] : null; $validAddressCacheKey = $this->getCacheKey($validAddress); $this->avaTaxLogger->addDebug('Loaded \\AvaTax\\ValidateResult from SOAP.', ['request' => var_export($validateRequest, true), 'result' => var_export($validateResult, true)]); $this->cache->save($serializedValidateResult, $validAddressCacheKey, [Config::AVATAX_CACHE_TAG]); } catch (LocalizedException $e) { $this->avaTaxLogger->addDebug('\\AvaTax\\ValidateResult no valid address found from SOAP.', ['result' => var_export($validateResult, true)]); } catch (\SoapFault $e) { $this->avaTaxLogger->error("Exception: \n" . $e->getMessage() . "\n" . $e->faultstring, ['request' => var_export($addressService->__getLastRequest(), true), 'result' => var_export($addressService->__getLastResponse(), true)]); throw $e; } return $validateResult; }
/** * Log page * * @return \Magento\Backend\Model\View\Result\Page */ public function execute() { // Initiate Log Clearing try { $this->logTask->clearLogs(); if ($this->logTask->getDeleteCount() > 0) { $message = __('%1 log records were cleared.', $this->logTask->getDeleteCount()); // Display message on the page $this->messageManager->addSuccess($message); } else { // Display message on the page $this->messageManager->addSuccess(__('No logs needed to be cleared.')); } } catch (\Exception $e) { // Build error message $message = __('An error occurred while clearing the log.'); // Display error message on the page $this->messageManager->addErrorMessage($message . "\n" . __('Error Message: ') . $e->getMessage()); // Log the exception $this->avaTaxLogger->error($message, ['exception' => sprintf('Exception message: %s%sTrace: %s', $e->getMessage(), "\n", $e->getTraceAsString())]); } // Redirect browser to log list page /** @var Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); $resultRedirect->setPath('*/*/'); return $resultRedirect; }
/** * Log page * * @return \Magento\Backend\Model\View\Result\Page */ public function execute() { // Initiate Queue Processing of pending queued entities try { $this->queueTask->clearQueue(); if ($this->queueTask->getDeleteCompleteCount() > 0) { $message = __('%1 (completed) queued records were cleared. ', $this->queueTask->getDeleteCompleteCount()); // Display message on the page $this->messageManager->addSuccess($message); } if ($this->queueTask->getDeleteFailedCount() > 0) { $message = __('%1 (failed) queued records were cleared. ', $this->queueTask->getDeleteFailedCount()); // Display message on the page $this->messageManager->addSuccess($message); } } catch (\Exception $e) { // Build error message $message = __('An error occurred while clearing the queue.'); // Display error message on the page $this->messageManager->addErrorMessage($message . "\n" . __('Error Message: ') . $e->getMessage()); // Log the exception $this->avaTaxLogger->error($message, ['exception' => sprintf('Exception message: %s%sTrace: %s', $e->getMessage(), "\n", $e->getTraceAsString())]); } // Redirect browser to queue list page /** @var Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); $resultRedirect->setPath('*/*/'); return $resultRedirect; }
/** * Get an AvaTax address object with fields as specified in data * * Note: AvaTax only allows 3 street fields according to the API documentation. The customer/address/street_lines * allows the admin to create fields for 1 to 4 street lines. This configuration is currently disabled in * Magento/CustomerCustomAttributes/etc/adminhtml/system.xml. As a result not currently doing anything with this. * Likely no special consideration since the code is already sending all addresses (up to 3) to AvaTax if present. * * @param $data \Magento\Customer\Api\Data\AddressInterface|\Magento\Quote\Api\Data\AddressInterface|\Magento\Sales\Api\Data\OrderAddressInterface|array * @return \AvaTax\Address * @throws LocalizedException */ public function getAddress($data) { switch (true) { case $data instanceof \Magento\Customer\Api\Data\AddressInterface: $data = $this->convertCustomerAddressToAvaTaxAddress($data); break; case $data instanceof \Magento\Quote\Api\Data\AddressInterface: $data = $this->convertQuoteAddressToAvaTaxAddress($data); break; case $data instanceof \Magento\Sales\Api\Data\OrderAddressInterface: $data = $this->convertOrderAddressToAvaTaxAddress($data); break; case !is_array($data): throw new LocalizedException(__('Input parameter "$data" was not of a recognized/valid type: "%1".', [gettype($data)])); } if (isset($data['RegionId'])) { $data['Region'] = $this->getRegionCodeById($data['RegionId']); unset($data['RegionId']); } try { $data = $this->metaDataObject->validateData($data); } catch (MetaData\ValidationException $e) { $this->avaTaxLogger->error('Error validating address: ' . $e->getMessage(), ['data' => var_export($data, true)]); // Rethrow exception as if internal validation fails, don't send address to AvaTax throw $e; } $address = $this->addressFactory->create(); return $this->populateAddress($data, $address); }
/** * Clear the queue of complete and failed records */ public function clearQueue() { $this->avaTaxLogger->debug(__('Starting queue clearing')); $this->clearCompleteQueue(); $this->clearFailedQueue(); $this->avaTaxLogger->debug(__('Finished queue clearing'), ['delete_complete_count' => $this->deleteCompleteCount, 'delete_failed_count' => $this->deleteFailedCount]); }
/** * Cache validated response * * @param GetTaxRequest $getTaxRequest * @param $storeId * @param bool $useCache * @return GetTaxResult * @throws LocalizedException */ public function getTax(GetTaxRequest $getTaxRequest, $storeId, $useCache = false) { $cacheKey = $this->getCacheKey($getTaxRequest) . $storeId; $getTaxResult = @unserialize($this->cache->load($cacheKey)); if ($getTaxResult instanceof GetTaxResult && $useCache) { $this->avaTaxLogger->addDebug('Loaded \\AvaTax\\GetTaxResult from cache.', ['result' => var_export($getTaxResult, true), 'cache_key' => $cacheKey]); return $getTaxResult; } $getTaxResult = $this->taxInteraction->getTaxService($this->type, $storeId)->getTax($getTaxRequest); $this->avaTaxLogger->addDebug('Loaded \\AvaTax\\GetTaxResult from SOAP.', ['request' => var_export($getTaxRequest, true), 'result' => var_export($getTaxResult, true)]); // Only cache successful requests if ($useCache && $getTaxResult->getResultCode() == \AvaTax\SeverityLevel::$Success) { $serializedGetTaxResult = serialize($getTaxResult); $this->cache->save($serializedGetTaxResult, $cacheKey, [Config::AVATAX_CACHE_TAG], self::CACHE_LIFETIME); } return $getTaxResult; }
/** * Process Queue * * @return Redirect */ public function execute() { // Initiate Queue Processing of pending queued entities try { $this->queueTask->processPendingQueue(); $message = __('The queue was successfully processed. ') . __('%1 queued records were processed. ', $this->queueTask->getProcessCount()); $this->messageManager->addSuccess($message); if ($this->queueTask->getErrorCount() > 0) { $errorMessage = __('Some queue records received errors while processing. ') . __('%1 queued records had errors. ', $this->queueTask->getErrorCount()); // Include the error messages from the queue task foreach ($this->queueTask->getErrorMessages() as $queueErrorMessage) { $errorMessage .= $queueErrorMessage; } // Display error message on the page $this->messageManager->addErrorMessage($errorMessage); } // Check for any queue records that appear to have been hung and reset them $this->queueTask->resetHungQueuedRecords(); if ($this->queueTask->getResetCount() > 0) { $errorMessage = __('Some queue records appeared to have been abandoned while processing. ') . __('%1 queued records were reset to pending so they can be retried. ', $this->queueTask->getResetCount()); // Display error message on the page $this->messageManager->addErrorMessage($errorMessage); } } catch (\Exception $e) { // Build error message $message = __('An error occurred while processing the queue. '); $partialSuccess = ''; if ($this->queueTask->getProcessCount() > 0) { $partialSuccess = ' ' . __('%1 queued records were processed. ', $this->queueTask->getProcessCount()); } // Display error message on the page $this->messageManager->addErrorMessage($message . $partialSuccess . __('Error Message: ') . $e->getMessage()); // Log the exception $this->avaTaxLogger->error($message, ['exception' => sprintf('Exception message: %s%sTrace: %s', $e->getMessage(), "\n", $e->getTraceAsString()), 'process_count' => var_export($this->queueTask->getProcessCount(), true)]); } // Redirect browser to queue list page /** @var Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); $resultRedirect->setPath('*/*/'); return $resultRedirect; }
/** * Clear the queue of complete and failed records */ public function clearLogs() { $this->avaTaxLogger->debug(__('Starting queue clearing')); /** @var $collection \ClassyLlama\AvaTax\Model\ResourceModel\Log\Collection */ $collection = $this->logCollectionFactory->create(); // Get configuration for record lifetime $lifetimeDays = $this->avaTaxConfig->getLogDbLifetime(); // Calculate the number of seconds to adjust the filter // 86400 seconds == 60 seconds * 60 minutes * 24 hours == 1 day $secondsBeforeNow = $lifetimeDays * 60 * 60 * 24; // Add filters $collection->addCreatedAtBeforeFilter($secondsBeforeNow); // Process each queued entity /** @var $log Log */ foreach ($collection as $log) { // Remove the queue record $log->delete(); $this->deleteCount++; } $this->avaTaxLogger->debug(__('Finished clearing log entries'), ['delete_count' => $this->deleteCount]); }
/** * Convert quote/order/invoice/creditmemo to the AvaTax object and request tax from the Get Tax API * * @param \Magento\Quote\Model\Quote $quote * @param \Magento\Tax\Api\Data\QuoteDetailsInterface $taxQuoteDetails * @param \Magento\Tax\Api\Data\QuoteDetailsInterface $baseTaxQuoteDetails * @param \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment * @return \Magento\Tax\Api\Data\TaxDetailsInterface[] * @throws \ClassyLlama\AvaTax\Exception\TaxCalculationException * @throws \Exception */ public function getTaxDetailsForQuote(\Magento\Quote\Model\Quote $quote, \Magento\Tax\Api\Data\QuoteDetailsInterface $taxQuoteDetails, \Magento\Tax\Api\Data\QuoteDetailsInterface $baseTaxQuoteDetails, \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment) { $storeId = $quote->getStoreId(); $taxService = $this->taxService; try { // Total quantity of an item can be determined by multiplying parent * child quantity, so it's necessary // to calculate total quantities on a list of all items $this->taxCalculation->calculateTotalQuantities($taxQuoteDetails->getItems()); $this->taxCalculation->calculateTotalQuantities($baseTaxQuoteDetails->getItems()); // Taxes need to be calculated on the base prices/amounts, not the current currency prices. As a result of this, // only the $baseTaxQuoteDetails will have taxes calculated for it. The taxes for the current currency will be // calculated by multiplying the base tax rates * currency conversion rate. /** @var $getTaxRequest GetTaxRequest */ $getTaxRequest = $this->interactionTax->getGetTaxRequestForQuote($quote, $baseTaxQuoteDetails, $shippingAssignment); if (is_null($getTaxRequest)) { $message = __('$quote was empty or address was not valid so not running getTax request.'); throw new \ClassyLlama\AvaTax\Exception\TaxCalculationException($message); } $getTaxResult = $taxService->getTax($getTaxRequest, $storeId, true); if ($getTaxResult->getResultCode() == \AvaTax\SeverityLevel::$Success) { $store = $quote->getStore(); $baseTaxDetails = $this->taxCalculation->calculateTaxDetails($baseTaxQuoteDetails, $getTaxResult, true, $store); /** * If quote is using a currency other than the base currency, calculate tax details for both quote * currency and base currency. Otherwise use the same tax details object. */ if ($quote->getBaseCurrencyCode() != $quote->getQuoteCurrencyCode()) { $taxDetails = $this->taxCalculation->calculateTaxDetails($taxQuoteDetails, $getTaxResult, false, $store); } else { $taxDetails = $baseTaxDetails; } return [self::KEY_TAX_DETAILS => $taxDetails, self::KEY_BASE_TAX_DETAILS => $baseTaxDetails]; } else { $message = __('Bad result code: %1', $getTaxResult->getResultCode()); $this->avaTaxLogger->warning($message, ['request' => var_export($getTaxRequest, true), 'result' => var_export($getTaxResult, true)]); throw new \ClassyLlama\AvaTax\Exception\TaxCalculationException($message); } } catch (\SoapFault $exception) { $message = "Exception: \n"; if ($exception) { $message .= $exception->faultstring; } $message .= $taxService->__getLastRequest() . "\n"; $message .= $taxService->__getLastResponse() . "\n"; $this->avaTaxLogger->error("Exception: \n" . $exception ? $exception->faultstring : "", ['request' => var_export($taxService->__getLastRequest(), true), 'result' => var_export($taxService->__getLastResponse(), true)]); throw new \ClassyLlama\AvaTax\Exception\TaxCalculationException($message); } catch (\Exception $exception) { $message = $exception->getMessage(); $this->avaTaxLogger->error($message); throw new \ClassyLlama\AvaTax\Exception\TaxCalculationException($message); } }
/** * @param int $storeId * @param string $entityTypeCode * @param int $entityId * @param string $incrementId * @param string $queueStatus * @throws \Exception * @throws \Magento\Framework\Exception\LocalizedException */ public function build($storeId, $entityTypeCode, $entityId, $incrementId, $queueStatus) { // validating $entityTypeCode if (!in_array($entityTypeCode, [self::ENTITY_TYPE_CODE_INVOICE, self::ENTITY_TYPE_CODE_CREDITMEMO])) { $message = __('When building a queue record an invalid entity_type_code was provided'); $this->avaTaxLogger->error($message, ['invalid_entity_type_code' => $entityTypeCode]); throw new \Exception($message); } // Get Entity Type Details $entityType = $this->eavConfig->getEntityType($entityTypeCode); $this->setStoreId($storeId); $this->setEntityTypeId($entityType->getEntityTypeId()); $this->setEntityTypeCode($entityTypeCode); $this->setEntityId($entityId); $this->setIncrementId($incrementId); $this->setQueueStatus($queueStatus); $this->setAttempts(0); }
/** * @param \Magento\Sales\Api\Data\InvoiceInterface|\Magento\Sales\Api\Data\CreditmemoInterface $entity * @param \ClassyLlama\AvaTax\Api\Data\GetTaxResponseInterface $processSalesResponse */ protected function updateAdditionalEntityAttributes($entity, GetTaxResponseInterface $processSalesResponse) { $entityExtension = $entity->getExtensionAttributes(); if ($entityExtension == null) { $entityExtension = $this->getEntityExtensionInterface($entity); } // check to see if the AvataxIsUnbalanced is already set on this entity $avataxIsUnbalancedToSave = false; if ($entityExtension->getAvataxIsUnbalanced() === null) { $entityExtension->setAvataxIsUnbalanced($processSalesResponse->getIsUnbalanced()); $avataxIsUnbalancedToSave = true; } else { // check to see if any existing value is different from the new value if ($processSalesResponse->getIsUnbalanced() != $entityExtension->getAvataxIsUnbalanced()) { // Log the warning $this->avaTaxLogger->warning(__('When processing an entity in the queue there was an existing AvataxIsUnbalanced and ' . 'the new value was different than the old one. The old value was overwritten.'), ['old_is_unbalanced' => $entityExtension->getAvataxIsUnbalanced(), 'new_is_unbalanced' => $processSalesResponse->getIsUnbalanced()]); $entityExtension->setAvataxIsUnbalanced($processSalesResponse->getIsUnbalanced()); $avataxIsUnbalancedToSave = true; } } // check to see if the BaseAvataxTaxAmount is already set on this entity $baseAvataxTaxAmountToSave = false; if ($entityExtension->getBaseAvataxTaxAmount() === null) { $entityExtension->setBaseAvataxTaxAmount($processSalesResponse->getBaseAvataxTaxAmount()); $baseAvataxTaxAmountToSave = true; } else { // check to see if any existing value is different from the new value if ($processSalesResponse->getBaseAvataxTaxAmount() != $entityExtension->getBaseAvataxTaxAmount()) { // Log the warning $this->avaTaxLogger->warning(__('When processing an entity in the queue there was an existing BaseAvataxTaxAmount and ' . 'the new value was different than the old one. The old value was overwritten.'), ['old_base_avatax_tax_amount' => $entityExtension->getBaseAvataxTaxAmount(), 'new_base_avatax_tax_amount' => $processSalesResponse->getBaseAvataxTaxAmount()]); $entityExtension->setBaseAvataxTaxAmount($processSalesResponse->getBaseAvataxTaxAmount()); $baseAvataxTaxAmountToSave = true; } } // save the ExtensionAttributes on the entity object if ($avataxIsUnbalancedToSave || $baseAvataxTaxAmountToSave) { $entity->setExtensionAttributes($entityExtension); // get the repository for this entity type $entityRepository = $this->getEntityRepository($entity); // save the entity object using the repository $entityRepository->save($entity); } }
/** * Accepts an invoice or creditmemo and returns an \AvaTax\Line object * * @param \Magento\Sales\Api\Data\InvoiceInterface|\Magento\Sales\Api\Data\CreditmemoInterface $data * @return \AvaTax\Line|bool */ public function getNegativeAdjustmentLine($data) { $amount = $data->getBaseAdjustmentNegative(); if ($amount == 0) { return false; } $storeId = $data->getStoreId(); $itemCode = $this->config->getSkuAdjustmentNegative($storeId); $data = ['No' => $this->getLineNumber(), 'ItemCode' => $itemCode, 'Description' => self::ADJUSTMENT_NEGATIVE_LINE_DESCRIPTION, 'Qty' => 1, 'Amount' => $amount, 'Discounted' => false, 'TaxIncluded' => true]; try { $data = $this->metaDataObject->validateData($data); } catch (ValidationException $e) { $this->avaTaxLogger->error('Error validating line: ' . $e->getMessage(), ['data' => var_export($data, true)]); } /** @var $line \AvaTax\Line */ $line = $this->lineFactory->create(); $this->populateLine($data, $line); return $line; }
/** * @param \Magento\Sales\Model\Spi\InvoiceResourceInterface $subject * @param \Closure $proceed * * I include both the extended AbstractModel and implemented Interface here for the IDE's benefit * @param \Magento\Framework\Model\AbstractModel|\Magento\Sales\Api\Data\InvoiceInterface $entity * @return \Magento\Sales\Model\Spi\InvoiceResourceInterface * @throws \Magento\Framework\Exception\CouldNotSaveException */ public function aroundSave(InvoiceResourceInterface $subject, \Closure $proceed, AbstractModel $entity) { // Check to see if this is a newly created entity and store the determination for later evaluation after // the entity is saved via plugin closure. After the entity is saved it will not be listed as new any longer. $isObjectNew = $entity->isObjectNew(); // Save AvaTax extension attributes if ($this->avaTaxConfig->isModuleEnabled($entity->getStoreId())) { // check to see if any extension attributes exist and set them on the model for saving to the db $extensionAttributes = $entity->getExtensionAttributes(); if ($extensionAttributes && $extensionAttributes->getAvataxIsUnbalanced() !== null) { $entity->setData('avatax_is_unbalanced', $extensionAttributes->getAvataxIsUnbalanced()); } if ($extensionAttributes && $extensionAttributes->getBaseAvataxTaxAmount() !== null) { $entity->setData('base_avatax_tax_amount', $extensionAttributes->getBaseAvataxTaxAmount()); } // Updating a field to trigger a change to the record when avatax_is_unbalanced and base_avatax_tax_amount // are both false or 0 which evaluate the same as null in the isModified check if ($extensionAttributes && ($extensionAttributes->getAvataxIsUnbalanced() !== null && ($entity->getOrigData('avatax_is_unbalanced') === null || $extensionAttributes->getAvataxIsUnbalanced() != $entity->getOrigData('avatax_is_unbalanced')) || $extensionAttributes->getBaseAvataxTaxAmount() !== null && ($entity->getOrigData('base_avatax_tax_amount') === null || $extensionAttributes->getBaseAvataxTaxAmount() != $entity->getOrigData('base_avatax_tax_amount')))) { $entity->setUpdatedAt($this->dateTime->gmtDate()); } } /** @var \Magento\Sales\Model\Spi\InvoiceResourceInterface $resultEntity */ $resultEntity = $proceed($entity); /** @var \Magento\Sales\Model\Order $order */ $order = $entity->getOrder(); $isVirtual = $order->getIsVirtual(); $address = $isVirtual ? $entity->getBillingAddress() : $entity->getShippingAddress(); $storeId = $entity->getStoreId(); // Queue the entity to be sent to AvaTax if ($this->avaTaxConfig->isModuleEnabled($entity->getStoreId()) && $this->avaTaxConfig->getTaxMode($entity->getStoreId()) == Config::TAX_MODE_ESTIMATE_AND_SUBMIT && $this->avaTaxConfig->isAddressTaxable($address, $storeId)) { // Add this entity to the avatax processing queue if this is a new entity if ($isObjectNew) { /** @var Queue $queue */ $queue = $this->queueFactory->create(); $queue->build($entity->getStoreId(), Queue::ENTITY_TYPE_CODE_INVOICE, $entity->getEntityId(), $entity->getIncrementId(), Queue::QUEUE_STATUS_PENDING); $queue->save(); $this->avaTaxLogger->debug(__('Added entity to the queue'), ['queue_id' => $queue->getId(), 'entity_type_code' => Queue::ENTITY_TYPE_CODE_INVOICE, 'entity_id' => $entity->getEntityId()]); } } return $resultEntity; }
public function aroundSaveAddressInformation(\Magento\Checkout\Model\ShippingInformationManagement $subject, \Closure $proceed, $cartId, ShippingInformationInterface $addressInformation) { // Only validate address if module is enabled $quote = $this->quoteRepository->getActive($cartId); $storeId = $quote->getStoreId(); if (!$this->config->isModuleEnabled($storeId)) { $paymentDetails = $proceed($cartId, $addressInformation); $this->ensureTaxCalculationSuccess($storeId); return $paymentDetails; } // Only validate address if address validation is enabled if (!$this->config->isAddressValidationEnabled($storeId)) { $paymentDetails = $proceed($cartId, $addressInformation); $this->ensureTaxCalculationSuccess($storeId); return $paymentDetails; } // If quote is virtual, getShippingAddress will return billing address, so no need to check if quote is virtual $shippingAddress = $addressInformation->getShippingAddress(); $shippingInformationExtension = $addressInformation->getExtensionAttributes(); $errorMessage = null; $validAddress = null; $customerAddress = null; $quoteAddress = null; $shouldValidateAddress = true; if (!is_null($shippingInformationExtension)) { $shouldValidateAddress = $shippingInformationExtension->getShouldValidateAddress(); } $customerAddressId = $shippingAddress->getCustomerAddressId(); $enabledAddressValidationCountries = explode(',', $this->config->getAddressValidationCountriesEnabled($storeId)); if (!in_array($shippingAddress->getCountryId(), $enabledAddressValidationCountries)) { $shouldValidateAddress = false; } if ($shouldValidateAddress) { try { $validAddress = $this->validationInteraction->validateAddress($shippingAddress, $storeId); } catch (AddressValidateException $e) { $errorMessage = $e->getMessage(); } catch (\SoapFault $e) { // If there is a SoapFault, it will have already been logged, so just disable address validation, as we // don't want to display SoapFault error message to user $shouldValidateAddress = false; } catch (\Exception $e) { $this->avaTaxLogger->error('Error in validating address in aroundSaveAddressInformation: ' . $e->getMessage()); // Continue without address validation $shouldValidateAddress = false; } } // Determine which address to save to the customer or shipping addresses if (!is_null($validAddress)) { $quoteAddress = $validAddress; } else { $quoteAddress = $shippingAddress; } try { /* * Regardless of whether address was validated by AvaTax, if the address is a customer address then we need * to save that address on the customer record. The reason for this is that when a user is on the "Review * & Payments" step and they are selecting between "Valid" and "Original" address options, the selected * address information is submitted to this API so that the customer address is updated and tax * calculation is affected accordingly. */ if ($customerAddressId) { // Update the customer address $customerAddress = $this->customerAddressRepository->getById($customerAddressId); $mergedCustomerAddress = $this->addressInteraction->copyQuoteAddressToCustomerAddress($quoteAddress, $customerAddress); $this->customerAddressRepository->save($mergedCustomerAddress); } else { // Update the shipping address $addressInformation->setShippingAddress($quoteAddress); } } catch (\Exception $e) { // There may be scenarios in which the above address updating may fail, in which case we should just do // nothing $this->avaTaxLogger->error('Error in saving address: ' . $e->getMessage()); // Continue without address validation $shouldValidateAddress = false; } $returnValue = $proceed($cartId, $addressInformation); $this->ensureTaxCalculationSuccess($storeId); if (!$shouldValidateAddress) { return $returnValue; } $paymentDetailsExtension = $returnValue->getExtensionAttributes(); if (is_null($paymentDetailsExtension)) { $paymentDetailsExtension = $this->paymentDetailsExtensionFactory->create(); } if (!is_null($validAddress)) { $paymentDetailsExtension->setValidAddress($validAddress); } else { $paymentDetailsExtension->setErrorMessage($errorMessage); } $paymentDetailsExtension->setOriginalAddress($shippingAddress); $returnValue->setExtensionAttributes($paymentDetailsExtension); return $returnValue; }
/** * Creates and returns a populated getTaxRequest for a invoice * * @param \Magento\Sales\Api\Data\InvoiceInterface|\Magento\Sales\Api\Data\CreditmemoInterface $object * @return GetTaxRequest */ public function getGetTaxRequestForSalesObject($object) { $order = $this->orderRepository->get($object->getOrderId()); $lines = []; $items = $object->getItems(); $this->taxClassHelper->populateCorrectTaxClasses($items, $object->getStoreId()); /** @var \Magento\Tax\Api\Data\QuoteDetailsItemInterface $item */ foreach ($items as $item) { $line = $this->interactionLine->getLine($item); if ($line) { $lines[] = $line; } } $objectIsCreditMemo = $object instanceof \Magento\Sales\Api\Data\CreditmemoInterface; $credit = $objectIsCreditMemo; $line = $this->interactionLine->getShippingLine($object, $credit); if ($line) { $lines[] = $line; } $line = $this->interactionLine->getGiftWrapItemsLine($object, $credit); if ($line) { $lines[] = $line; } $line = $this->interactionLine->getGiftWrapOrderLine($object, $credit); if ($line) { $lines[] = $line; } $line = $this->interactionLine->getGiftWrapCardLine($object, $credit); if ($line) { $lines[] = $line; } if ($objectIsCreditMemo) { $line = $this->interactionLine->getPositiveAdjustmentLine($object); if ($line) { $lines[] = $line; } $line = $this->interactionLine->getNegativeAdjustmentLine($object); if ($line) { $lines[] = $line; } } /** @var \Magento\Sales\Api\Data\OrderAddressInterface $address */ if (!$order->getIsVirtual()) { $address = $order->getShippingAddress(); } else { $address = $order->getBillingAddress(); } $avaTaxAddress = $this->address->getAddress($address); $store = $this->storeRepository->getById($object->getStoreId()); $currentDate = $this->getFormattedDate($store, $object->getCreatedAt()); $taxOverride = null; if ($object instanceof \Magento\Sales\Api\Data\InvoiceInterface) { $docType = DocumentType::$SalesInvoice; if ($this->areTimesDifferentDays($order->getCreatedAt(), $object->getCreatedAt(), $object->getStoreId())) { $taxCalculationDate = $this->getFormattedDate($store, $order->getCreatedAt()); // Set the tax date for calculation $taxOverride = $this->taxOverrideFactory->create(); $taxOverride->setTaxDate($taxCalculationDate); $taxOverride->setTaxOverrideType(\AvaTax\TaxOverrideType::$TaxDate); $taxOverride->setTaxAmount(0.0); $taxOverride->setReason(self::AVATAX_INVOICE_OVERRIDE_REASON); } } else { $docType = DocumentType::$ReturnInvoice; $invoice = $this->getInvoice($object->getInvoiceId()); // If a Creditmemo was generated for an invoice, use the created_at value from the invoice if ($invoice) { $taxCalculationDate = $this->getFormattedDate($store, $invoice->getCreatedAt()); } else { $taxCalculationDate = $this->getFormattedDate($store, $order->getCreatedAt()); } // Set the tax date for calculation $taxOverride = $this->taxOverrideFactory->create(); $taxOverride->setTaxDate($taxCalculationDate); $taxOverride->setTaxOverrideType(\AvaTax\TaxOverrideType::$TaxDate); $taxOverride->setTaxAmount(0.0); $taxOverride->setReason(self::AVATAX_CREDITMEMO_OVERRIDE_REASON); } $customer = $this->getCustomerById($order->getCustomerId()); $customerUsageType = $customer ? $this->taxClassHelper->getAvataxTaxCodeForCustomer($customer) : null; $data = ['StoreId' => $store->getId(), 'Commit' => $this->config->getCommitSubmittedTransactions($store), 'TaxOverride' => $taxOverride, 'CurrencyCode' => $order->getOrderCurrencyCode(), 'CustomerCode' => $this->getCustomerCode($order), 'CustomerUsageType' => $customerUsageType, 'DestinationAddress' => $avaTaxAddress, 'DocCode' => $object->getIncrementId() . '123-' . rand(10000000, 90000000000), 'DocDate' => $currentDate, 'DocType' => $docType, 'ExchangeRate' => $this->getExchangeRate($store, $order->getBaseCurrencyCode(), $order->getOrderCurrencyCode()), 'ExchangeRateEffDate' => $currentDate, 'Lines' => $lines, 'DetailLevel' => DetailLevel::$Document, 'PaymentDate' => $currentDate, 'PurchaseOrderNo' => $object->getIncrementId()]; $data = array_merge($this->retrieveGetTaxRequestFields($store, $address, $object), $data); try { $data = $this->metaDataObject->validateData($data); } catch (ValidationException $e) { $this->avaTaxLogger->error('Error validating data: ' . $e->getMessage(), ['data' => var_export($data, true)]); } /** @var $getTaxRequest GetTaxRequest */ $getTaxRequest = $this->getTaxRequestFactory->create(); $this->populateGetTaxRequest($data, $getTaxRequest); return $getTaxRequest; }