/**
  * 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 \Magento\Sales\Model\Spi\InvoiceResourceInterface $subject
  * @param \Closure $proceed
  *
  *        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
  * @param mixed $value
  * @param string $field field to load by (defaults to model id)
  * @return \Magento\Framework\Model\ResourceModel\Db\AbstractDb
  * @throws \Magento\Framework\Exception\NoSuchEntityException
  */
 public function aroundLoad(InvoiceResourceInterface $subject, \Closure $proceed, AbstractModel $entity, $value, $field = null)
 {
     /** @var \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resultEntity */
     $resultEntity = $proceed($entity, $value, $field);
     // Load AvaTax extension attributes
     if ($this->avaTaxConfig->isModuleEnabled($entity->getStoreId())) {
         // Get the AvaTax Attributes from the AbstractModel
         $avataxIsUnbalanced = $entity->getData('avatax_is_unbalanced');
         $baseAvataxTaxAmount = $entity->getData('base_avatax_tax_amount');
         // Check the AvaTax Entity to see if we need to add extension attributes
         if ($avataxIsUnbalanced !== null || $baseAvataxTaxAmount !== null) {
             // Get any existing extension attributes or create a new one
             $entityExtension = $entity->getExtensionAttributes();
             if (!$entityExtension) {
                 $entityExtension = $this->invoiceExtensionFactory->create();
             }
             // Set the attributes
             if ($avataxIsUnbalanced !== null) {
                 $entityExtension->setAvataxIsUnbalanced($avataxIsUnbalanced);
             }
             if ($baseAvataxTaxAmount !== null) {
                 $entityExtension->setBaseAvataxTaxAmount($baseAvataxTaxAmount);
             }
             // save the ExtensionAttributes on the entity object
             $entity->setExtensionAttributes($entityExtension);
         }
     }
     return $resultEntity;
 }
 /**
  * Ping AvaTax using configured live/production mode
  *
  * @param $scopeId
  * @param $scopeType
  * @return array
  */
 protected function sendPing($scopeId, $scopeType)
 {
     $errors = [];
     if (!$this->config->isModuleEnabled($scopeId, $scopeType)) {
         return $errors;
     }
     $message = '';
     $type = $this->config->getLiveMode() ? Config::API_PROFILE_NAME_PROD : Config::API_PROFILE_NAME_DEV;
     try {
         $result = $this->interactionTax->getTaxService($type, $scopeId, $scopeType)->ping();
         if (is_object($result) && $result->getResultCode() != \AvaTax\SeverityLevel::$Success) {
             foreach ($result->getMessages() as $messages) {
                 $message .= $messages->getName() . ': ' . $messages->getSummary() . "\n";
             }
         } elseif (is_object($result) && $result->getResultCode() == \AvaTax\SeverityLevel::$Success) {
             $this->messageManager->addSuccess(__('Successfully connected to AvaTax using the ' . '<a href="#row_tax_avatax_connection_settings_header">%1 credentials</a>', $type));
         }
     } catch (\Exception $exception) {
         $message = $exception->getMessage();
     }
     if ($message) {
         $errors[] = __('Error connecting to AvaTax using the ' . '<a href="#row_tax_avatax_connection_settings_header">%1 credentials</a>: %2', $type, $message);
     }
     return $errors;
 }
 /**
  * Overrides payment component and adds config variable to be used in the component and template
  *
  * This class takes the place of a layout config change to checkout_index_index.xml. Making the changes to the
  * layout this way is necessary because in the process of merging the layout files, layout overrides are
  * applied over existing nodes in alphabetical order by Namespace_ModuleName. So Magento_Checkout overrides
  * ClassyLlama_AvaTax because Magento_Checkout layout files are merged after ClassyLlama_AvaTax layout files. The
  * solution is to set the value of the converted object after the layout files have been merged. Additionally,
  * because the config fields must be accessed from PHP, the most efficient method of setting the config node values
  * is with PHP as the following code does.
  *
  * @param array $jsLayout
  * @return array
  */
 public function process($jsLayout)
 {
     if ($this->config->isModuleEnabled()) {
         if ($this->config->isAddressValidationEnabled($this->storeManager->getStore())) {
             $userHasChoice = $this->config->allowUserToChooseAddress($this->storeManager->getStore());
             if ($userHasChoice) {
                 $jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children']['payment']['config']['instructions'] = $this->config->getAddressValidationInstructionsWithChoice($this->storeManager->getStore());
             } else {
                 $jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children']['payment']['config']['instructions'] = $this->config->getAddressValidationInstructionsWithoutChoice($this->storeManager->getStore());
             }
             $jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children']['payment']['config']['errorInstructions'] = $this->config->getAddressValidationErrorInstructions($this->storeManager->getStore());
             $jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children']['payment']['config']['choice'] = $userHasChoice;
             $jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children']['payment']['component'] = self::COMPONENT_PATH;
         }
     }
     return $jsLayout;
 }
 /**
  * Check to see if there are any native tax rules created that may affect AvaTax
  *
  * @return array
  */
 public function checkNativeTaxRules()
 {
     $errors = [];
     if ($this->avaTaxConfig->isModuleEnabled() && $this->avaTaxConfig->getTaxMode($this->storeManager->getDefaultStoreView()) != Config::TAX_MODE_NO_ESTIMATE_OR_SUBMIT && !$this->avaTaxConfig->isNativeTaxRulesIgnored()) {
         $taxRules = $this->taxRuleRepository->getList($this->searchCriteriaBuilder->create());
         if (count($taxRules->getItems())) {
             $errors[] = __('You have %1 native Magento Tax Rule(s) configured. ' . 'Please <a href="%2">review the tax rule(s)</a> and delete any that you do not specifically want enabled. ' . 'You should only have rules setup if you want to use them as backup rules in case of AvaTax ' . 'errors (see <a href="#row_tax_avatax_error_handling_header">Error Action setting</a>) ' . 'or if you need to support VAT tax. ' . '<a href="%3">Ignore this notification</a>.', count($taxRules->getItems()), $this->backendUrl->getUrl('tax/rule'), $this->backendUrl->getUrl('avatax/tax/ignoreTaxRuleNotification'));
         }
     }
     return $errors;
 }
 /**
  * Check whether notification is displayed
  *
  * @return bool
  */
 public function isDisplayed()
 {
     // Check configuration to see if this should be evaluated further
     if ($this->avaTaxConfig->isModuleEnabled() == false || $this->avaTaxConfig->getTaxMode($this->storeManager->getDefaultStoreView()) != Config::TAX_MODE_ESTIMATE_AND_SUBMIT || $this->avaTaxConfig->getQueueAdminNotificationEnabled() == false) {
         return false;
     }
     // Query the database to get some stats about the queue
     $this->loadQueueStats();
     // Determine if we need to notify the admin user
     if ($this->authorization->isAllowed('ClassyLlama_AvaTax::manage_avatax') && $this->statQueueCount > 0) {
         return true;
     } else {
         return false;
     }
 }
Example #7
0
 /**
  * Map extra taxables associated with quote. Add AvaTax details to extension objects.
  *
  * @param QuoteDetailsItemInterfaceFactory $itemDataObjectFactory
  * @param Address $address
  * @param bool $useBaseCurrency
  * @return \Magento\Tax\Api\Data\QuoteDetailsItemInterface[]
  */
 public function mapQuoteExtraTaxables(QuoteDetailsItemInterfaceFactory $itemDataObjectFactory, Address $address, $useBaseCurrency)
 {
     $itemDataObjects = parent::mapQuoteExtraTaxables($itemDataObjectFactory, $address, $useBaseCurrency);
     $storeId = $address->getQuote()->getStore()->getId();
     if (!$this->config->isModuleEnabled($storeId) || $this->config->getTaxMode($storeId) == Config::TAX_MODE_NO_ESTIMATE_OR_SUBMIT || !$this->config->isAddressTaxable($address, $storeId)) {
         return $itemDataObjects;
     }
     foreach ($itemDataObjects as $itemDataObject) {
         switch ($itemDataObject->getType()) {
             case Giftwrapping::QUOTE_TYPE:
                 $itemCode = $this->config->getSkuGiftWrapOrder($storeId);
                 $taxCode = $this->taxClassHelper->getAvataxTaxCodeForGiftOptions($storeId);
                 $this->addExtensionAttributesToTaxQuoteDetailsItem($itemDataObject, $itemCode, $taxCode, \ClassyLlama\AvaTax\Framework\Interaction\Line::GIFT_WRAP_ORDER_LINE_DESCRIPTION);
                 break;
             case Giftwrapping::PRINTED_CARD_TYPE:
                 $itemCode = $this->config->getSkuShippingGiftWrapCard($storeId);
                 $taxCode = $this->taxClassHelper->getAvataxTaxCodeForGiftOptions($storeId);
                 $this->addExtensionAttributesToTaxQuoteDetailsItem($itemDataObject, $itemCode, $taxCode, \ClassyLlama\AvaTax\Framework\Interaction\Line::GIFT_WRAP_ORDER_LINE_DESCRIPTION);
                 break;
         }
     }
     return $itemDataObjects;
 }
 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;
 }
 /**
  * Check whether notification is displayed
  *
  * @return bool
  */
 public function isDisplayed()
 {
     return $this->getText() && $this->isTaxConfigPage() && $this->config->isModuleEnabled();
 }
 /**
  * Checks whether the given record will be handled by this handler.
  *
  * Uses the admin configuration settings to determine if the record should be handled
  *
  * @param array $record
  * @return Boolean
  */
 public function isHandling(array $record)
 {
     return $this->avaTaxConfig->isModuleEnabled() && $record['level'] >= $this->avaTaxConfig->getLogDbLevel();
 }
 /**
  * Check whether notification is displayed
  *
  * @return bool
  */
 public function isDisplayed()
 {
     return $this->getText() && $this->isQueuePage() && $this->config->isModuleEnabled() && $this->config->getTaxMode($this->storeManager->getDefaultStoreView()) != Config::TAX_MODE_ESTIMATE_AND_SUBMIT;
 }