Example #1
0
 /**
  * 2016-08-20
  * Цель плагина — установка флага DoTransaction.
  * Без этого флага возврат работать не будет:
  * https://github.com/magento/magento2/blob/2.1.0/app/code/Magento/Sales/Model/Order/Payment.php#L633
  * @see \Magento\Sales\Model\Order\Payment::refund()
  * Там стоит код:
  * if ($gateway->canRefund() && $creditmemo->getDoTransaction()) {
  *
  * Флаг DoTransaction устанавливался в прежних версиях Magento, в том числе в Magento 2.1:
  * https://github.com/magento/magento2/blob/2.1.0/app/code/Magento/Sales/Model/Service/CreditmemoService.php#L150
  * Однако в 2.2-dev он почему-то не устанавливается (видимо, баг):
  * https://github.com/magento/magento2/blob/522fad/app/code/Magento/Sales/Model/Service/CreditmemoService.php#L156-L189
  *
  * @see \Magento\Sales\Model\Service\CreditmemoService::refund()
  * @param Sb $sb
  * @param ICreditmemo|Creditmemo $creditmemo
  * @param bool $offlineRequested [optional]
  * @return array()
  */
 public function beforeRefund(Sb $sb, ICreditmemo $creditmemo, $offlineRequested = false)
 {
     if ($creditmemo instanceof Creditmemo) {
         /** @noinspection PhpUndefinedMethodInspection */
         $creditmemo->setDoTransaction(!$offlineRequested);
     }
     return [$creditmemo, $offlineRequested];
 }
 /**
  * @param \Magento\Sales\Api\Data\CreditmemoInterface $creditmemo
  * @return bool
  * @throws \Magento\Framework\Exception\LocalizedException
  */
 protected function validateForRefund(\Magento\Sales\Api\Data\CreditmemoInterface $creditmemo)
 {
     if ($creditmemo->getId()) {
         throw new \Magento\Framework\Exception\LocalizedException(__('We cannot register an existing credit memo.'));
     }
     $baseOrderRefund = $this->priceCurrency->round($creditmemo->getOrder()->getBaseTotalRefunded() + $creditmemo->getBaseGrandTotal());
     if ($baseOrderRefund > $this->priceCurrency->round($creditmemo->getOrder()->getBaseTotalPaid())) {
         $baseAvailableRefund = $creditmemo->getOrder()->getBaseTotalPaid() - $creditmemo->getOrder()->getBaseTotalRefunded();
         throw new \Magento\Framework\Exception\LocalizedException(__('The most money available to refund is %1.', $creditmemo->getOrder()->formatBasePrice($baseAvailableRefund)));
     }
     return true;
 }
 /**
  * @param Queue $queue
  * @param \Magento\Sales\Api\Data\InvoiceInterface|\Magento\Sales\Api\Data\CreditmemoInterface $entity
  * @param \ClassyLlama\AvaTax\Api\Data\GetTaxResponseInterface $processSalesResponse
  */
 protected function completeQueueProcessing(Queue $queue, $entity, GetTaxResponseInterface $processSalesResponse)
 {
     $message = __('%1 #%2 was submitted to AvaTax', ucfirst($queue->getEntityTypeCode()), $entity->getIncrementId());
     $queueMessage = '';
     if ($processSalesResponse->getIsUnbalanced()) {
         $adjustmentMessage = null;
         if ($entity instanceof CreditmemoInterface) {
             if (abs($entity->getBaseAdjustmentNegative()) > 0 || abs($entity->getBaseAdjustmentPositive()) > 0) {
                 $adjustmentMessage = __('The difference was at least partly caused by the fact that the creditmemo ' . 'contained an adjustment of %1 and Magento doesn\'t factor that into its calculation, ' . 'but AvaTax does.', $entity->getBaseAdjustment());
             }
         }
         $queueMessage = __('Unbalanced Response - Collected: %1, AvaTax Actual: %2', $entity->getBaseTaxAmount(), $processSalesResponse->getBaseAvataxTaxAmount());
         if ($adjustmentMessage) {
             $queueMessage .= ' — ' . $adjustmentMessage;
         }
         // add comment about unbalanced amount
         $message .= '<br/>' . __('When submitting the %1 to AvaTax the amount calculated for tax differed from what was' . ' recorded in Magento.', $queue->getEntityTypeCode()) . '<br/>' . __('There was a difference of %1', $entity->getBaseTaxAmount() - $processSalesResponse->getBaseAvataxTaxAmount()) . '<br/>';
         if ($adjustmentMessage) {
             $message .= '<strong>' . $adjustmentMessage . '</strong><br/>';
         }
         $message .= __('Magento listed a tax amount of %1', $entity->getBaseTaxAmount()) . '<br/>' . __('AvaTax calculated the tax to be %1', $processSalesResponse->getBaseAvataxTaxAmount()) . '<br/>';
     }
     $queue->setMessage($queueMessage);
     $queue->setQueueStatus(Queue::QUEUE_STATUS_COMPLETE);
     $queue->save();
     // Add comment to order
     $this->addOrderComment($entity->getOrderId(), $message);
 }
Example #4
0
 /**
  * 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;
 }
Example #5
0
 /**
  * Process invoice or credit memo
  *
  * @param \Magento\Sales\Api\Data\InvoiceInterface|\Magento\Sales\Api\Data\CreditmemoInterface $object
  * @return \ClassyLlama\AvaTax\Api\Data\GetTaxResponseInterface
  * @throws \ClassyLlama\AvaTax\Exception\TaxCalculationException
  */
 public function processSalesObject($object)
 {
     $storeId = $object->getStoreId();
     $taxService = $this->taxService;
     try {
         /** @var $getTaxRequest GetTaxRequest */
         $getTaxRequest = $this->interactionTax->getGetTaxRequestForSalesObject($object);
     } catch (\Exception $e) {
         $message = __('Error while building the request to send to AvaTax. ');
         $this->avaTaxLogger->error($message, ['entity_id' => $object->getEntityId(), 'object_class' => get_class($object), 'exception' => sprintf('Exception message: %s%sTrace: %s', $e->getMessage(), "\n", $e->getTraceAsString())]);
         throw new \ClassyLlama\AvaTax\Exception\TaxCalculationException($message . $e->getMessage(), $e->getCode(), $e);
     }
     if (is_null($getTaxRequest)) {
         $message = '$getTaxRequest was empty so not running getTax request.';
         $this->avaTaxLogger->warning($message);
         throw new \ClassyLlama\AvaTax\Exception\TaxCalculationException($message);
     }
     try {
         $getTaxResult = $taxService->getTax($getTaxRequest, $storeId);
         if ($getTaxResult->getResultCode() == \AvaTax\SeverityLevel::$Success) {
             // Since credit memo tax amounts come back from AvaTax as negative numbers, get absolute value
             $avataxTaxAmount = abs($getTaxResult->getTotalTax());
             $unbalanced = $avataxTaxAmount != $object->getBaseTaxAmount();
             /** @var $response \ClassyLlama\AvaTax\Api\Data\GetTaxResponseInterface */
             $response = $this->getTaxResponseFactory->create();
             $response->setIsUnbalanced($unbalanced)->setBaseAvataxTaxAmount($avataxTaxAmount);
             return $response;
         } else {
             $message = $this->getErrorMessageFromGetTaxResult($getTaxResult);
             $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->critical("Exception: \n" . $exception ? $exception->faultstring : "", ['request' => var_export($taxService->__getLastRequest(), true), 'result' => var_export($taxService->__getLastResponse(), true)]);
         throw new \ClassyLlama\AvaTax\Exception\TaxCalculationException($message);
     }
 }
Example #6
0
 /**
  * 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;
 }