TaxAddressResolver.php 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. <?php
  2. /**
  3. * @copyright Vertex. All rights reserved. https://www.vertexinc.com/
  4. * @author Mediotype https://www.mediotype.com/
  5. */
  6. namespace Vertex\Tax\Model\Calculation\VertexCalculator;
  7. use Magento\Quote\Api\Data\AddressInterface as QuoteAddressInterface;
  8. use Magento\Customer\Api\AccountManagementInterface;
  9. use Magento\Customer\Api\Data\RegionInterface;
  10. use Magento\Framework\Exception\NoSuchEntityException;
  11. use Magento\Framework\Exception\LocalizedException;
  12. /**
  13. * Resolves a taxable address candidate for use with a TaxQuoteRequest.
  14. *
  15. * This class is designed to be a replacement for {@see Magento\Tax\Model\Calculation::getRateRequest}. Since the core
  16. * rate request generator does not include some line-item specifics needed for accurate rate calculation, this resolver
  17. * prepares those additional details.
  18. */
  19. class TaxAddressResolver
  20. {
  21. /** @var AccountManagementInterface */
  22. private $customerAccountManagement;
  23. /**
  24. * @param AccountManagementInterface $customerAccountManagement
  25. */
  26. public function __construct(AccountManagementInterface $customerAccountManagement)
  27. {
  28. $this->customerAccountManagement = $customerAccountManagement;
  29. }
  30. /**
  31. * Retrieve a taxable address.
  32. *
  33. * @param QuoteAddressInterface|null $billingAddress
  34. * @param QuoteAddressInterface|null $shippingAddress
  35. * @param bool $isVirtual
  36. * @param int|null $customerId
  37. * @return QuoteAddressInterface
  38. */
  39. public function resolve(
  40. QuoteAddressInterface $billingAddress = null,
  41. QuoteAddressInterface $shippingAddress = null,
  42. $isVirtual = false,
  43. $customerId = null
  44. ) {
  45. /**
  46. * Vertex requires that we base tax on the billing address for virtual orders, ignoring core behavior.
  47. * @see \Magento\Tax\Model\Config::CONFIG_XML_PATH_BASED_ON
  48. */
  49. $address = $isVirtual ? $billingAddress: $shippingAddress;
  50. if ($address !== null && !$this->validate($address)) {
  51. $address = $isVirtual
  52. ? $this->getDefaultBillingAddress($address, $customerId)
  53. : $this->getDefaultShippingAddress($address, $customerId);
  54. }
  55. return $address;
  56. }
  57. /**
  58. * Prepare a new address clone from a given original.
  59. *
  60. * Clones are used to populate a given address with missing details without affecting the original. We do not want
  61. * to affect the original in any way. The clone acts as our proprietary "rate request" object.
  62. *
  63. * @param QuoteAddressInterface $originalAddress
  64. * @param QuoteAddressInterface|QuoteAddressInterface|null $newAddress
  65. * @return QuoteAddressInterface
  66. */
  67. private function cloneAddress(QuoteAddressInterface $originalAddress, $newAddress = null)
  68. {
  69. if ($newAddress === null) {
  70. return $originalAddress;
  71. }
  72. $clone = clone $originalAddress;
  73. $data = [
  74. 'id' => $originalAddress->getId(),
  75. 'country_id' => $newAddress->getCountryId(),
  76. 'region_id' => $newAddress->getRegionId(),
  77. 'region' => $newAddress->getRegion(),
  78. 'postcode' => $newAddress->getPostcode(),
  79. ];
  80. $this->setAddressData($clone, $data);
  81. return $clone;
  82. }
  83. /**
  84. * Retrieve the default customer billing address.
  85. *
  86. * @param QuoteAddressInterface $address
  87. * @param int|null $customerId
  88. * @return QuoteAddressInterface
  89. */
  90. private function getDefaultBillingAddress(QuoteAddressInterface $address, $customerId = null)
  91. {
  92. try {
  93. return $this->cloneAddress(
  94. $address,
  95. $this->customerAccountManagement->getDefaultBillingAddress($customerId)
  96. );
  97. } catch (NoSuchEntityException $error) {
  98. return $this->cloneAddress($address);
  99. } catch (LocalizedException $error) {
  100. return $this->cloneAddress($address);
  101. }
  102. }
  103. /**
  104. * Retrieve the default customer shipping address.
  105. *
  106. * @param QuoteAddressInterface $address
  107. * @param int|null $customerId
  108. * @return QuoteAddressInterface
  109. */
  110. private function getDefaultShippingAddress(QuoteAddressInterface $address, $customerId = null)
  111. {
  112. try {
  113. return $this->cloneAddress(
  114. $address,
  115. $this->customerAccountManagement->getDefaultShippingAddress($customerId)
  116. );
  117. } catch (NoSuchEntityException $error) {
  118. return $this->cloneAddress($address);
  119. } catch (LocalizedException $error) {
  120. return $this->cloneAddress($address);
  121. }
  122. }
  123. /**
  124. * Write the data set to the given address.
  125. *
  126. * @param QuoteAddressInterface $address
  127. * @param array $data
  128. * @return void
  129. */
  130. private function setAddressData(QuoteAddressInterface $address, array $data = [])
  131. {
  132. foreach ($data as $key => $value) {
  133. $method = 'set' . str_replace(' ', '', ucwords(str_replace('_', ' ', $key)));
  134. if (method_exists($address, $method)) {
  135. $address->{$method}($value);
  136. }
  137. }
  138. }
  139. /**
  140. * Determine whether an address can be used for tax calculation.
  141. *
  142. * @param QuoteAddressInterface|null $address
  143. * @return bool
  144. */
  145. private function validate(QuoteAddressInterface $address = null)
  146. {
  147. return $address !== null
  148. && $address->getCountryId()
  149. && (
  150. $address->getRegionId()
  151. || ($address->getRegion() instanceof RegionInterface && $address->getRegion()->getRegionId())
  152. )
  153. && $address->getPostcode();
  154. }
  155. }