OrderItemProcessor.php 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. <?php
  2. namespace Vertex\Tax\Model\Api\Data\InvoiceRequestBuilder;
  3. use Magento\Catalog\Api\Data\ProductInterface;
  4. use Magento\Catalog\Api\ProductRepositoryInterface;
  5. use Magento\Framework\Api\SearchCriteriaBuilder;
  6. use Magento\Framework\Api\SearchCriteriaBuilderFactory;
  7. use Magento\Sales\Api\Data\OrderInterface;
  8. use Vertex\Data\LineItemInterfaceFactory;
  9. use Vertex\Services\Invoice\RequestInterface;
  10. use Vertex\Tax\Model\Repository\TaxClassNameRepository;
  11. /**
  12. * Processes Items on an Order and adds them to a Vertex Invoice's LineItems
  13. */
  14. class OrderItemProcessor implements OrderProcessorInterface
  15. {
  16. /** @var TaxClassNameRepository */
  17. private $classNameRepository;
  18. /** @var SearchCriteriaBuilderFactory */
  19. private $criteriaBuilderFactory;
  20. /** @var LineItemInterfaceFactory */
  21. private $lineItemFactory;
  22. /** @var ProductRepositoryInterface */
  23. private $productRepository;
  24. /**
  25. * @param LineItemInterfaceFactory $lineItemFactory
  26. * @param ProductRepositoryInterface $productRepository
  27. * @param SearchCriteriaBuilderFactory $criteriaBuilderFactory
  28. * @param TaxClassNameRepository $classNameRepository
  29. */
  30. public function __construct(
  31. LineItemInterfaceFactory $lineItemFactory,
  32. ProductRepositoryInterface $productRepository,
  33. SearchCriteriaBuilderFactory $criteriaBuilderFactory,
  34. TaxClassNameRepository $classNameRepository
  35. ) {
  36. $this->lineItemFactory = $lineItemFactory;
  37. $this->productRepository = $productRepository;
  38. $this->criteriaBuilderFactory = $criteriaBuilderFactory;
  39. $this->classNameRepository = $classNameRepository;
  40. }
  41. /**
  42. * @inheritdoc
  43. */
  44. public function process(RequestInterface $request, OrderInterface $order)
  45. {
  46. $lineItems = [];
  47. $orderItems = [];
  48. $productIds = [];
  49. /** @var int[] $taxClasses Key is OrderItem ID, Value is Tax Class ID */
  50. $taxClasses = [];
  51. foreach ($order->getItems() as $item) {
  52. if ($item->getBaseRowTotal() === null) {
  53. continue;
  54. }
  55. $orderItems[$item->getItemId()] = $item;
  56. $productIds[] = $item->getProductId();
  57. }
  58. $products = $this->getProductsIndexedById($productIds);
  59. foreach ($orderItems as $item) {
  60. if (in_array($item->getProductType(), ['bundle', 'configurable'])) {
  61. // Bundle's component parts are available with correct data
  62. // Configurables are handled on the child level with getParentItem for pricing data
  63. continue;
  64. }
  65. $parentItem = $item->getParentItem();
  66. if ($parentItem && $parentItem->getProductType() === 'configurable') {
  67. // The child of a configurable does not have the pricing information
  68. $unitPrice = $parentItem->getBasePrice();
  69. $extendedPrice = $parentItem->getBaseRowTotal() - $parentItem->getBaseDiscountAmount();
  70. } else {
  71. $unitPrice = $item->getBasePrice();
  72. $extendedPrice = $item->getBaseRowTotal() - $item->getBaseDiscountAmount();
  73. }
  74. $product = $products[$item->getProductId()];
  75. $taxClassAttribute = $product->getCustomAttribute('tax_class_id');
  76. $taxClassId = $taxClassAttribute ? $taxClassAttribute->getValue() : 0;
  77. $taxClasses[$item->getItemId()] = $taxClassId;
  78. $lineItem = $this->lineItemFactory->create();
  79. $lineItem->setProductCode($item->getSku());
  80. $lineItem->setQuantity($item->getQtyOrdered());
  81. $lineItem->setUnitPrice($unitPrice);
  82. $lineItem->setExtendedPrice($extendedPrice);
  83. $lineItem->setLineItemId($item->getItemId());
  84. $lineItems[] = $lineItem;
  85. }
  86. /** @var string[int] $taxClassNames Tax Classes indexed by ID */
  87. $taxClassNames = $this->classNameRepository->getListByIds(array_values($taxClasses));
  88. foreach ($lineItems as $lineItem) {
  89. $lineItemId = $lineItem->getLineItemId();
  90. $taxClass = $taxClasses[$lineItemId];
  91. $taxClassName = $taxClassNames[$taxClass];
  92. $lineItem->setProductClass($taxClassName);
  93. }
  94. $request->setLineItems(array_merge($request->getLineItems(), $lineItems));
  95. return $request;
  96. }
  97. /**
  98. * Retrieve an array of products indexed by their ID
  99. *
  100. * @param int[] $productIds
  101. * @return ProductInterface[] Indexed by id
  102. */
  103. private function getProductsIndexedById(array $productIds)
  104. {
  105. /** @var SearchCriteriaBuilder $criteriaBuilder */
  106. $criteriaBuilder = $this->criteriaBuilderFactory->create();
  107. $criteriaBuilder->addFilter('entity_id', $productIds, 'in');
  108. $criteria = $criteriaBuilder->create();
  109. $items = $this->productRepository->getList($criteria)->getItems();
  110. /** @var ProductInterface[] $products */
  111. return array_reduce(
  112. $items,
  113. function (array $carry, ProductInterface $product) {
  114. // This ensures that all products are indexed by ID, it is not an API guarantee
  115. $carry[$product->getId()] = $product;
  116. return $carry;
  117. },
  118. []
  119. );
  120. }
  121. }