Order.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702
  1. <?php
  2. namespace Dotdigitalgroup\Email\Model\Connector;
  3. /**
  4. * Transactional data for orders, including mapped custom order attributes to sync.
  5. *
  6. * @SuppressWarnings(PHPMD.TooManyFields)
  7. */
  8. class Order
  9. {
  10. /**
  11. * Order Increment ID.
  12. *
  13. * @var string
  14. */
  15. public $id;
  16. /**
  17. * Email.
  18. *
  19. * @var string
  20. */
  21. public $email;
  22. /**
  23. * @var int
  24. */
  25. public $quoteId;
  26. /**
  27. * @var string
  28. */
  29. public $storeName;
  30. /**
  31. * @var string
  32. */
  33. public $purchaseDate;
  34. /**
  35. * @var array
  36. */
  37. public $deliveryAddress = [];
  38. /**
  39. * @var array
  40. */
  41. public $billingAddress = [];
  42. /**
  43. * @var array
  44. */
  45. public $products = [];
  46. /**
  47. * @var float
  48. */
  49. public $orderSubtotal;
  50. /**
  51. * @var float
  52. */
  53. public $discountAmount;
  54. /**
  55. * @var float
  56. */
  57. public $orderTotal;
  58. /**
  59. * Payment name.
  60. *
  61. * @var string
  62. */
  63. public $payment;
  64. /**
  65. * @var string
  66. */
  67. public $deliveryMethod;
  68. /**
  69. * @var float
  70. */
  71. public $deliveryTotal;
  72. /**
  73. * @var string
  74. */
  75. public $currency;
  76. /**
  77. * @var object
  78. */
  79. public $couponCode;
  80. /**
  81. * @var array
  82. */
  83. public $custom = [];
  84. /**
  85. * @var string
  86. */
  87. public $orderStatus;
  88. /**
  89. * @var \Dotdigitalgroup\Email\Helper\Data
  90. */
  91. public $helper;
  92. /**
  93. * @var \Magento\Customer\Model\CustomerFactory
  94. */
  95. public $customerFactory;
  96. /**
  97. * @var \Magento\Catalog\Model\ProductFactory
  98. */
  99. public $productFactory;
  100. /**
  101. * @var \Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory
  102. */
  103. public $attributeCollection;
  104. /**
  105. * @var \Magento\Eav\Model\Entity\Attribute\SetFactory
  106. */
  107. public $setFactory;
  108. /**
  109. * @var \Magento\Catalog\Model\ResourceModel\Product
  110. */
  111. private $productResource;
  112. /**
  113. * @var \Magento\Framework\Stdlib\StringUtils
  114. */
  115. private $stringUtils;
  116. /**
  117. * Order constructor.
  118. *
  119. * @param \Magento\Eav\Model\Entity\Attribute\SetFactory $setFactory
  120. * @param \Magento\Eav\Api\AttributeSetRepositoryInterface $attributeSet
  121. * @param \Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory $attributeCollection
  122. * @param \Magento\Catalog\Model\ProductFactory $productFactory
  123. * @param \Magento\Catalog\Model\ResourceModel\Product $productResource
  124. * @param \Magento\Customer\Model\CustomerFactory $customerFactory
  125. * @param \Dotdigitalgroup\Email\Helper\Data $helperData
  126. * @param \Magento\Store\Model\StoreManagerInterface $storeManagerInterface
  127. * @param \Magento\Framework\Stdlib\StringUtils $stringUtils
  128. */
  129. public function __construct(
  130. \Magento\Eav\Model\Entity\Attribute\SetFactory $setFactory,
  131. \Magento\Eav\Api\AttributeSetRepositoryInterface $attributeSet,
  132. \Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory $attributeCollection,
  133. \Magento\Catalog\Model\ProductFactory $productFactory,
  134. \Magento\Catalog\Model\ResourceModel\Product $productResource,
  135. \Magento\Customer\Model\CustomerFactory $customerFactory,
  136. \Dotdigitalgroup\Email\Helper\Data $helperData,
  137. \Magento\Store\Model\StoreManagerInterface $storeManagerInterface,
  138. \Magento\Framework\Stdlib\StringUtils $stringUtils
  139. ) {
  140. $this->attributeSet = $attributeSet;
  141. $this->setFactory = $setFactory;
  142. $this->attributeCollection = $attributeCollection;
  143. $this->productFactory = $productFactory;
  144. $this->customerFactory = $customerFactory;
  145. $this->helper = $helperData;
  146. $this->productResource = $productResource;
  147. $this->_storeManager = $storeManagerInterface;
  148. $this->stringUtils = $stringUtils;
  149. }
  150. /**
  151. * Set the order data information.
  152. *
  153. * @param \Magento\Sales\Model\Order $orderData
  154. *
  155. * @return $this
  156. */
  157. public function setOrderData($orderData)
  158. {
  159. $this->id = $orderData->getIncrementId();
  160. $this->email = $orderData->getCustomerEmail();
  161. $this->quoteId = $orderData->getQuoteId();
  162. $this->storeName = $orderData->getStoreName();
  163. $this->purchaseDate = $orderData->getCreatedAt();
  164. $this->deliveryMethod = $orderData->getShippingDescription();
  165. $this->deliveryTotal = (float)number_format(
  166. $orderData->getShippingAmount(),
  167. 2,
  168. '.',
  169. ''
  170. );
  171. $this->currency = $orderData->getStoreCurrencyCode();
  172. $payment = $orderData->getPayment();
  173. if ($payment) {
  174. if ($payment->getMethod()) {
  175. $methodInstance = $payment->getMethodInstance($payment->getMethod());
  176. if ($methodInstance) {
  177. $this->payment = $methodInstance->getTitle();
  178. }
  179. }
  180. }
  181. $this->couponCode = $orderData->getCouponCode();
  182. /*
  183. * custom order attributes
  184. */
  185. $website = $this->_storeManager->getStore($orderData->getStore())->getWebsite();
  186. $customAttributes
  187. = $this->helper->getConfigSelectedCustomOrderAttributes(
  188. $website
  189. );
  190. if ($customAttributes) {
  191. $fields = $this->helper->getOrderTableDescription();
  192. $this->custom = [];
  193. foreach ($customAttributes as $customAttribute) {
  194. if (isset($fields[$customAttribute])) {
  195. $field = $fields[$customAttribute];
  196. $value = $this->_getCustomAttributeValue(
  197. $field,
  198. $orderData
  199. );
  200. if ($value) {
  201. $this->_assignCustom($field, $value);
  202. }
  203. }
  204. }
  205. }
  206. /*
  207. * Billing address.
  208. */
  209. $this->processBillingAddress($orderData);
  210. /*
  211. * Shipping address.
  212. */
  213. $this->processShippingAddress($orderData);
  214. $syncCustomOption = $this->helper->getWebsiteConfig(
  215. \Dotdigitalgroup\Email\Helper\Config::XML_PATH_CONNECTOR_SYNC_ORDER_PRODUCT_CUSTOM_OPTIONS,
  216. $website
  217. );
  218. /*
  219. * Order items.
  220. */
  221. $this->processOrderItems($orderData, $syncCustomOption);
  222. $this->orderSubtotal = (float)number_format(
  223. $orderData->getData('subtotal'),
  224. 2,
  225. '.',
  226. ''
  227. );
  228. $this->discountAmount = (float)number_format(
  229. $orderData->getData('discount_amount'),
  230. 2,
  231. '.',
  232. ''
  233. );
  234. $orderTotal = abs(
  235. $orderData->getData('grand_total') - $orderData->getTotalRefunded()
  236. );
  237. $this->orderTotal = (float)number_format($orderTotal, 2, '.', '');
  238. $this->orderStatus = $orderData->getStatus();
  239. unset($this->_storeManager);
  240. return $this;
  241. }
  242. /**
  243. * @param \Magento\Sales\Model\Order $orderData
  244. *
  245. * @return null
  246. */
  247. private function processBillingAddress($orderData)
  248. {
  249. if ($orderData->getBillingAddress()) {
  250. $billingData = $orderData->getBillingAddress()->getData();
  251. $this->billingAddress = [
  252. 'billing_address_1' => $this->_getStreet(
  253. $billingData['street'],
  254. 1
  255. ),
  256. 'billing_address_2' => $this->_getStreet(
  257. $billingData['street'],
  258. 2
  259. ),
  260. 'billing_city' => $billingData['city'],
  261. 'billing_region' => $billingData['region'],
  262. 'billing_country' => $billingData['country_id'],
  263. 'billing_postcode' => $billingData['postcode'],
  264. ];
  265. }
  266. }
  267. /**
  268. * @param \Magento\Sales\Model\Order $orderData
  269. *
  270. * @return null
  271. */
  272. private function processShippingAddress($orderData)
  273. {
  274. if ($orderData->getShippingAddress()) {
  275. $shippingData = $orderData->getShippingAddress()->getData();
  276. $this->deliveryAddress = [
  277. 'delivery_address_1' => $this->_getStreet(
  278. $shippingData['street'],
  279. 1
  280. ),
  281. 'delivery_address_2' => $this->_getStreet(
  282. $shippingData['street'],
  283. 2
  284. ),
  285. 'delivery_city' => $shippingData['city'],
  286. 'delivery_region' => $shippingData['region'],
  287. 'delivery_country' => $shippingData['country_id'],
  288. 'delivery_postcode' => $shippingData['postcode'],
  289. ];
  290. }
  291. }
  292. /**
  293. * @param \Magento\Sales\Model\Order $orderData
  294. * @param boolean $syncCustomOption
  295. *
  296. * @return null
  297. */
  298. private function processOrderItems($orderData, $syncCustomOption)
  299. {
  300. foreach ($orderData->getAllItems() as $productItem) {
  301. //product custom options
  302. $customOptions = [];
  303. if ($syncCustomOption) {
  304. $customOptions = $this->_getOrderItemOptions($productItem);
  305. }
  306. $productModel = $productItem->getProduct();
  307. if ($productModel) {
  308. // category names
  309. $categoryCollection = $productModel->getCategoryCollection()
  310. ->addAttributeToSelect('name');
  311. $productCat = [];
  312. foreach ($categoryCollection as $cat) {
  313. $categories = [];
  314. $categories[] = $cat->getName();
  315. $productCat[]['Name'] = $this->limitLength(
  316. implode(', ', $categories)
  317. );
  318. }
  319. $attributes = [];
  320. //selected attributes from config
  321. $configAttributes = $this->helper->getWebsiteConfig(
  322. \Dotdigitalgroup\Email\Helper\Config::XML_PATH_CONNECTOR_SYNC_ORDER_PRODUCT_ATTRIBUTES,
  323. $orderData->getStore()->getWebsite()
  324. );
  325. if ($configAttributes) {
  326. $configAttributes = explode(',', $configAttributes);
  327. //attributes from attribute set
  328. $attributesFromAttributeSet = $this->_getAttributesArray(
  329. $productModel->getAttributeSetId()
  330. );
  331. $attributes = $this->processConfigAttributes(
  332. $configAttributes,
  333. $attributesFromAttributeSet,
  334. $productModel,
  335. $attributes
  336. );
  337. }
  338. $attributeSetName = $this->getAttributeSetName($productModel);
  339. $productData = [
  340. 'name' => $productItem->getName(),
  341. 'sku' => $productItem->getSku(),
  342. 'qty' => (int)number_format(
  343. $productItem->getData('qty_ordered'),
  344. 2
  345. ),
  346. 'price' => (float)number_format(
  347. $productItem->getPrice(),
  348. 2,
  349. '.',
  350. ''
  351. ),
  352. 'attribute-set' => $attributeSetName,
  353. 'categories' => $productCat,
  354. 'attributes' => $attributes,
  355. 'custom-options' => $customOptions,
  356. ];
  357. if (!$customOptions) {
  358. unset($productData['custom-options']);
  359. }
  360. $this->products[] = $productData;
  361. } else {
  362. // when no product information is available limit to this data
  363. $productData = [
  364. 'name' => $productItem->getName(),
  365. 'sku' => $productItem->getSku(),
  366. 'qty' => (int)number_format(
  367. $productItem->getData('qty_ordered'),
  368. 2
  369. ),
  370. 'price' => (float)number_format(
  371. $productItem->getPrice(),
  372. 2,
  373. '.',
  374. ''
  375. ),
  376. 'attribute-set' => '',
  377. 'categories' => [],
  378. 'attributes' => [],
  379. 'custom-options' => $customOptions,
  380. ];
  381. if (!$customOptions) {
  382. unset($productData['custom-options']);
  383. }
  384. $this->products[] = $productData;
  385. }
  386. }
  387. }
  388. /**
  389. * @param mixed $configAttributes
  390. * @param mixed $attributesFromAttributeSet
  391. * @param mixed $productModel
  392. * @param mixed $attributes
  393. *
  394. * @return array
  395. */
  396. private function processConfigAttributes($configAttributes, $attributesFromAttributeSet, $productModel, $attributes)
  397. {
  398. foreach ($configAttributes as $attributeCode) {
  399. //if config attribute is in attribute set
  400. if (in_array($attributeCode, $attributesFromAttributeSet)) {
  401. //attribute input type
  402. $inputType = $this->productResource
  403. ->getAttribute($attributeCode)
  404. ->getFrontend()
  405. ->getInputType();
  406. //fetch attribute value from product depending on input type
  407. switch ($inputType) {
  408. case 'multiselect':
  409. case 'select':
  410. case 'dropdown':
  411. $value = $productModel->getAttributeText($attributeCode);
  412. break;
  413. case 'date':
  414. $value = $productModel->getData($attributeCode);
  415. break;
  416. default:
  417. $value = $productModel->getData($attributeCode);
  418. break;
  419. }
  420. $attributes = $this->processAttributeValue($attributes, $value, $attributeCode);
  421. }
  422. }
  423. return $attributes;
  424. }
  425. /**
  426. * @param array $attributes
  427. * @param string|array $value
  428. * @param string $attributeCode
  429. *
  430. * @return array
  431. */
  432. private function processAttributeValue($attributes, $value, $attributeCode)
  433. {
  434. if ($value && !is_array($value)) {
  435. // check limit on text and assign value to array
  436. $attributes[][$attributeCode] = $this->limitLength($value);
  437. } elseif ($value && is_array($value)) {
  438. $values = (isset($value['values']))? implode(',', $value['values']) : implode(',', $value);
  439. if ($values) {
  440. $attributes[][$attributeCode] = $this->limitLength($values);
  441. }
  442. }
  443. return $attributes;
  444. }
  445. /**
  446. * Get the street name by line number.
  447. *
  448. * @param string $street
  449. * @param int $line
  450. *
  451. * @return string
  452. */
  453. public function _getStreet($street, $line)
  454. {
  455. $street = explode("\n", $street);
  456. if ($line == 1) {
  457. return $street[0];
  458. }
  459. if (isset($street[$line - 1])) {
  460. return $street[$line - 1];
  461. } else {
  462. return '';
  463. }
  464. }
  465. /**
  466. * Exposes the class as an array of objects.
  467. * Return any exposed data that will included into the import as transactinoal data for Orders.
  468. *
  469. * @return array
  470. */
  471. public function expose()
  472. {
  473. $properties = array_diff_key(
  474. get_object_vars($this),
  475. array_flip([
  476. '_storeManager',
  477. 'helper',
  478. 'customerFactory',
  479. 'productFactory',
  480. 'attributeCollection',
  481. 'setFactory',
  482. 'attributeSet',
  483. 'productResource'
  484. ])
  485. );
  486. //remove null/0/false values
  487. $properties = array_filter($properties);
  488. return $properties;
  489. }
  490. /**
  491. * Get attrubute value for the field.
  492. *
  493. * @param array $field
  494. * @param \Magento\Sales\Model\Order $orderData
  495. *
  496. * @return float|int|null|string
  497. */
  498. public function _getCustomAttributeValue($field, $orderData)
  499. {
  500. $type = $field['DATA_TYPE'];
  501. $function = 'get';
  502. $exploded = explode('_', $field['COLUMN_NAME']);
  503. foreach ($exploded as $one) {
  504. $function .= ucfirst($one);
  505. }
  506. $value = null;
  507. try {
  508. switch ($type) {
  509. case 'int':
  510. case 'smallint':
  511. $value = (int)$orderData->$function();
  512. break;
  513. case 'decimal':
  514. $value = (float)number_format(
  515. $orderData->$function(),
  516. 2,
  517. '.',
  518. ''
  519. );
  520. break;
  521. case 'timestamp':
  522. case 'datetime':
  523. case 'date':
  524. $value = $orderData->$function();
  525. break;
  526. default:
  527. $value = $orderData->$function();
  528. }
  529. } catch (\Exception $e) {
  530. $this->helper->debug((string)$e, []);
  531. }
  532. return $value;
  533. }
  534. /**
  535. * Create property on runtime.
  536. *
  537. * @param string $field
  538. * @param mixed $value
  539. *
  540. * @return null
  541. */
  542. public function _assignCustom($field, $value)
  543. {
  544. $this->custom[$field['COLUMN_NAME']] = $value;
  545. }
  546. /**
  547. * Get attributes from attribute set.
  548. *
  549. * @param int $attributeSetId
  550. *
  551. * @return array
  552. */
  553. public function _getAttributesArray($attributeSetId)
  554. {
  555. $result = [];
  556. $attributes = $this->attributeCollection->create()
  557. ->setAttributeSetFilter($attributeSetId)
  558. ->getItems();
  559. foreach ($attributes as $attribute) {
  560. $result[] = $attribute->getAttributeCode();
  561. }
  562. return $result;
  563. }
  564. /**
  565. * Check string length and limit to 250.
  566. *
  567. * @param string $value
  568. *
  569. * @return string
  570. */
  571. private function limitLength($value)
  572. {
  573. if ($this->stringUtils->strlen($value) > \Dotdigitalgroup\Email\Helper\Data::DM_FIELD_LIMIT) {
  574. $value = mb_substr($value, 0, \Dotdigitalgroup\Email\Helper\Data::DM_FIELD_LIMIT);
  575. }
  576. return $value;
  577. }
  578. /**
  579. * @param \Magento\Sales\Model\Order\Item $orderItem
  580. * @return array
  581. */
  582. public function _getOrderItemOptions($orderItem)
  583. {
  584. $orderItemOptions = $orderItem->getProductOptions();
  585. //if product doesn't have options
  586. if (!array_key_exists('options', $orderItemOptions)) {
  587. return [];
  588. }
  589. $orderItemOptions = $orderItemOptions['options'];
  590. //if product options isn't array
  591. if (!is_array($orderItemOptions)) {
  592. return [];
  593. }
  594. $options = [];
  595. foreach ($orderItemOptions as $orderItemOption) {
  596. if (array_key_exists('value', $orderItemOption)
  597. && array_key_exists(
  598. 'label',
  599. $orderItemOption
  600. )
  601. ) {
  602. $label = str_replace(
  603. ' ',
  604. '-',
  605. $orderItemOption['label']
  606. );
  607. $options[][$label] = $orderItemOption['value'];
  608. }
  609. }
  610. return $options;
  611. }
  612. /**
  613. * @param \Magento\Catalog\Model\Product $product
  614. * @return string
  615. */
  616. public function getAttributeSetName($product)
  617. {
  618. $attributeSetRepository = $this->attributeSet->get($product->getAttributeSetId());
  619. return $attributeSetRepository->getAttributeSetName();
  620. }
  621. }