QuotationRequestBuilder.php 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  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\Api\Data;
  7. use Magento\Framework\Exception\LocalizedException;
  8. use Magento\Framework\Exception\NoSuchEntityException;
  9. use Magento\Store\Model\ScopeInterface;
  10. use Magento\Store\Model\StoreManagerInterface;
  11. use Magento\Tax\Api\Data\QuoteDetailsInterface;
  12. use Magento\Tax\Api\Data\QuoteDetailsItemInterface;
  13. use Magento\Tax\Api\Data\TaxClassKeyInterface;
  14. use Vertex\Data\LineItemInterface;
  15. use Vertex\Services\Quote\RequestInterface;
  16. use Vertex\Services\Quote\RequestInterfaceFactory;
  17. use Vertex\Tax\Model\AddressDeterminer;
  18. use Vertex\Tax\Model\Api\Utility\DeliveryTerm;
  19. use Vertex\Tax\Model\Config;
  20. use Vertex\Tax\Model\DateTimeImmutableFactory;
  21. /**
  22. * Builds a Quotation Request for the Vertex SDK
  23. */
  24. class QuotationRequestBuilder
  25. {
  26. const TRANSACTION_TYPE = 'SALE';
  27. /** @var AddressDeterminer */
  28. private $addressDeterminer;
  29. /** @var Config */
  30. private $config;
  31. /** @var CustomerBuilder */
  32. private $customerBuilder;
  33. /** @var DateTimeImmutableFactory */
  34. private $dateTimeFactory;
  35. /** @var DeliveryTerm */
  36. private $deliveryTerm;
  37. /** @var LineItemBuilder */
  38. private $lineItemBuilder;
  39. /** @var RequestInterfaceFactory */
  40. private $requestFactory;
  41. /** @var SellerBuilder */
  42. private $sellerBuilder;
  43. /** @var StoreManagerInterface */
  44. private $storeManager;
  45. /**
  46. * @param LineItemBuilder $lineItemBuilder
  47. * @param RequestInterfaceFactory $requestFactory
  48. * @param CustomerBuilder $customerBuilder
  49. * @param SellerBuilder $sellerBuilder
  50. * @param Config $config
  51. * @param DeliveryTerm $deliveryTerm
  52. * @param DateTimeImmutableFactory $dateTimeFactory
  53. * @param AddressDeterminer $addressDeterminer
  54. * @param StoreManagerInterface $storeManager
  55. */
  56. public function __construct(
  57. LineItemBuilder $lineItemBuilder,
  58. RequestInterfaceFactory $requestFactory,
  59. CustomerBuilder $customerBuilder,
  60. SellerBuilder $sellerBuilder,
  61. Config $config,
  62. DeliveryTerm $deliveryTerm,
  63. DateTimeImmutableFactory $dateTimeFactory,
  64. AddressDeterminer $addressDeterminer,
  65. StoreManagerInterface $storeManager
  66. ) {
  67. $this->lineItemBuilder = $lineItemBuilder;
  68. $this->requestFactory = $requestFactory;
  69. $this->customerBuilder = $customerBuilder;
  70. $this->sellerBuilder = $sellerBuilder;
  71. $this->config = $config;
  72. $this->deliveryTerm = $deliveryTerm;
  73. $this->dateTimeFactory = $dateTimeFactory;
  74. $this->addressDeterminer = $addressDeterminer;
  75. $this->storeManager = $storeManager;
  76. }
  77. /**
  78. * Create a properly formatted Quote Request for the Vertex API
  79. *
  80. * @param QuoteDetailsInterface $quoteDetails
  81. * @param string|null $scopeCode
  82. * @return RequestInterface
  83. * @throws LocalizedException
  84. * @throws NoSuchEntityException
  85. */
  86. public function buildFromQuoteDetails(QuoteDetailsInterface $quoteDetails, $scopeCode = null)
  87. {
  88. /** @var RequestInterface $request */
  89. $request = $this->requestFactory->create();
  90. $request->setShouldReturnAssistedParameters(true);
  91. $request->setDocumentDate($this->dateTimeFactory->create());
  92. $request->setTransactionType(static::TRANSACTION_TYPE);
  93. $request->setCurrencyCode($this->storeManager->getStore($scopeCode)->getBaseCurrencyCode());
  94. $taxLineItems = $this->getLineItemData($quoteDetails->getItems());
  95. $request->setLineItems($taxLineItems);
  96. $address = $this->addressDeterminer->determineAddress(
  97. $quoteDetails->getShippingAddress() ?: $quoteDetails->getBillingAddress(),
  98. $quoteDetails->getCustomerId(),
  99. $this->isVirtual($quoteDetails)
  100. );
  101. $seller = $this->sellerBuilder
  102. ->setScopeCode($scopeCode)
  103. ->setScopeType(ScopeInterface::SCOPE_STORE)
  104. ->build();
  105. $request->setSeller($seller);
  106. $taxClassKey = $quoteDetails->getCustomerTaxClassKey();
  107. if ($taxClassKey && $taxClassKey->getType() === TaxClassKeyInterface::TYPE_ID) {
  108. $customerTaxClassId = $taxClassKey->getValue();
  109. } else {
  110. $customerTaxClassId = $quoteDetails->getCustomerTaxClassId();
  111. }
  112. $request->setCustomer(
  113. $this->customerBuilder->buildFromCustomerAddress(
  114. $address,
  115. $quoteDetails->getCustomerId(),
  116. $customerTaxClassId,
  117. $scopeCode
  118. )
  119. );
  120. $this->deliveryTerm->addIfApplicable($request);
  121. if ($this->config->getLocationCode($scopeCode)) {
  122. $request->setLocationCode($this->config->getLocationCode($scopeCode));
  123. }
  124. return $request;
  125. }
  126. /**
  127. * Build Line Items for the Request
  128. *
  129. * @param QuoteDetailsItemInterface[] $items
  130. * @return LineItemInterface[]
  131. */
  132. private function getLineItemData(array $items)
  133. {
  134. // The resulting LineItemInterface[] to be used with Vertex
  135. $taxLineItems = [];
  136. // An array of codes for parent items
  137. $parentCodes = [];
  138. // A map of all items by their code
  139. $itemMap = [];
  140. // Item codes already processed - to prevent duplicates from bundles & configurables
  141. $processedItems = [];
  142. foreach ($items as $item) {
  143. $itemMap[$item->getCode()] = $item;
  144. if ($item->getParentCode()) {
  145. $parentCodes[] = $item->getParentCode();
  146. }
  147. }
  148. foreach ($items as $item) {
  149. if (in_array($item->getCode(), array_merge($parentCodes, $processedItems), true)) {
  150. // We merge these two arrays together as a convenience so we only need to run in_array once
  151. continue;
  152. }
  153. $quantity = $item->getParentCode()
  154. ? $item->getQuantity() * $itemMap[$item->getParentCode()]->getQuantity()
  155. : $item->getQuantity();
  156. $taxLineItems[] = $this->lineItemBuilder->buildFromQuoteDetailsItem($item, $quantity);
  157. $processedItems[] = $item->getCode();
  158. }
  159. return $taxLineItems;
  160. }
  161. /**
  162. * Determine if the Quote is virtual
  163. *
  164. * @param QuoteDetailsInterface $quoteDetails
  165. * @return bool
  166. */
  167. private function isVirtual(QuoteDetailsInterface $quoteDetails)
  168. {
  169. foreach ($quoteDetails->getItems() as $item) {
  170. if ($item->getType() === 'shipping') {
  171. return false;
  172. }
  173. }
  174. return true;
  175. }
  176. }