Create.php 68 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Sales\Model\AdminOrder;
  7. use Magento\Customer\Api\AddressMetadataInterface;
  8. use Magento\Customer\Model\Metadata\Form as CustomerForm;
  9. use Magento\Framework\Api\ExtensibleDataObjectConverter;
  10. use Magento\Framework\App\ObjectManager;
  11. use Magento\Quote\Model\Quote\Address;
  12. use Magento\Quote\Model\Quote\Item;
  13. use Magento\Sales\Api\Data\OrderAddressInterface;
  14. use Magento\Sales\Model\Order;
  15. use Magento\Store\Model\StoreManagerInterface;
  16. use Psr\Log\LoggerInterface;
  17. /**
  18. * Order create model
  19. * @api
  20. * @SuppressWarnings(PHPMD.TooManyFields)
  21. * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
  22. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  23. * @since 100.0.2
  24. */
  25. class Create extends \Magento\Framework\DataObject implements \Magento\Checkout\Model\Cart\CartInterface
  26. {
  27. /**
  28. * Xml default email domain path
  29. */
  30. const XML_PATH_DEFAULT_EMAIL_DOMAIN = 'customer/create_account/email_domain';
  31. /**
  32. * Quote session object
  33. *
  34. * @var \Magento\Backend\Model\Session\Quote
  35. */
  36. protected $_session;
  37. /**
  38. * Quote customer wishlist model object
  39. *
  40. * @var \Magento\Wishlist\Model\Wishlist
  41. */
  42. protected $_wishlist;
  43. /**
  44. * Sales Quote instance
  45. *
  46. * @var \Magento\Quote\Model\Quote
  47. */
  48. protected $_cart;
  49. /**
  50. * Catalog Compare List instance
  51. *
  52. * @var \Magento\Catalog\Model\Product\Compare\ListCompare
  53. */
  54. protected $_compareList;
  55. /**
  56. * Re-collect quote flag
  57. *
  58. * @var boolean
  59. */
  60. protected $_needCollect;
  61. /**
  62. * Re-collect cart flag
  63. *
  64. * @var boolean
  65. */
  66. protected $_needCollectCart = false;
  67. /**
  68. * Collect (import) data and validate it flag
  69. *
  70. * @var boolean
  71. */
  72. protected $_isValidate = false;
  73. /**
  74. * Array of validate errors
  75. *
  76. * @var array
  77. */
  78. protected $_errors = [];
  79. /**
  80. * Quote associated with the model
  81. *
  82. * @var \Magento\Quote\Model\Quote
  83. */
  84. protected $_quote;
  85. /**
  86. * Core registry
  87. *
  88. * @var \Magento\Framework\Registry
  89. */
  90. protected $_coreRegistry = null;
  91. /**
  92. * @var \Psr\Log\LoggerInterface
  93. */
  94. protected $_logger;
  95. /**
  96. * Core event manager proxy
  97. *
  98. * @var \Magento\Framework\Event\ManagerInterface
  99. */
  100. protected $_eventManager = null;
  101. /**
  102. * @var \Magento\Sales\Model\Config
  103. */
  104. protected $_salesConfig;
  105. /**
  106. * @var \Magento\Framework\ObjectManagerInterface
  107. */
  108. protected $_objectManager;
  109. /**
  110. * @var \Magento\Framework\DataObject\Copy
  111. */
  112. protected $_objectCopyService;
  113. /**
  114. * @var \Magento\Framework\Message\ManagerInterface
  115. */
  116. protected $messageManager;
  117. /**
  118. * @var Product\Quote\Initializer
  119. */
  120. protected $quoteInitializer;
  121. /**
  122. * @var \Magento\Customer\Api\CustomerRepositoryInterface
  123. */
  124. protected $customerRepository;
  125. /**
  126. * @var \Magento\Customer\Api\AddressRepositoryInterface
  127. */
  128. protected $addressRepository;
  129. /**
  130. * @var \Magento\Customer\Api\Data\AddressInterfaceFactory
  131. */
  132. protected $addressFactory;
  133. /**
  134. * @var \Magento\Customer\Model\Metadata\FormFactory
  135. */
  136. protected $_metadataFormFactory;
  137. /**
  138. * @var \Magento\Customer\Api\GroupRepositoryInterface
  139. */
  140. protected $groupRepository;
  141. /**
  142. * @var \Magento\Framework\App\Config\ScopeConfigInterface
  143. */
  144. protected $_scopeConfig;
  145. /**
  146. * @var \Magento\CatalogInventory\Api\StockRegistryInterface
  147. */
  148. protected $stockRegistry;
  149. /**
  150. * @var \Magento\Sales\Model\AdminOrder\EmailSender
  151. */
  152. protected $emailSender;
  153. /**
  154. * @var \Magento\Quote\Model\Quote\Item\Updater
  155. */
  156. protected $quoteItemUpdater;
  157. /**
  158. * @var \Magento\Framework\DataObject\Factory
  159. */
  160. protected $objectFactory;
  161. /**
  162. * @var \Magento\Customer\Api\AccountManagementInterface
  163. */
  164. protected $accountManagement;
  165. /**
  166. * @var \Magento\Customer\Api\Data\CustomerInterfaceFactory
  167. */
  168. protected $customerFactory;
  169. /**
  170. * Constructor
  171. *
  172. * @var \Magento\Customer\Model\Customer\Mapper
  173. */
  174. protected $customerMapper;
  175. /**
  176. * @var \Magento\Quote\Api\CartRepositoryInterface
  177. */
  178. protected $quoteRepository;
  179. /**
  180. * @var \Magento\Quote\Api\CartManagementInterface
  181. */
  182. protected $quoteManagement;
  183. /**
  184. * @var \Magento\Framework\Api\DataObjectHelper
  185. */
  186. protected $dataObjectHelper;
  187. /**
  188. * @var \Magento\Sales\Api\OrderManagementInterface
  189. */
  190. protected $orderManagement;
  191. /**
  192. * @var \Magento\Quote\Model\QuoteFactory
  193. */
  194. protected $quoteFactory;
  195. /**
  196. * Serializer interface instance.
  197. *
  198. * @var \Magento\Framework\Serialize\Serializer\Json
  199. */
  200. private $serializer;
  201. /**
  202. * @var ExtensibleDataObjectConverter
  203. */
  204. private $dataObjectConverter;
  205. /**
  206. * @var StoreManagerInterface
  207. */
  208. private $storeManager;
  209. /**
  210. * @param \Magento\Framework\ObjectManagerInterface $objectManager
  211. * @param \Magento\Framework\Event\ManagerInterface $eventManager
  212. * @param \Magento\Framework\Registry $coreRegistry
  213. * @param \Magento\Sales\Model\Config $salesConfig
  214. * @param \Magento\Backend\Model\Session\Quote $quoteSession
  215. * @param \Psr\Log\LoggerInterface $logger
  216. * @param \Magento\Framework\DataObject\Copy $objectCopyService
  217. * @param \Magento\Framework\Message\ManagerInterface $messageManager
  218. * @param Product\Quote\Initializer $quoteInitializer
  219. * @param \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository
  220. * @param \Magento\Customer\Api\AddressRepositoryInterface $addressRepository
  221. * @param \Magento\Customer\Api\Data\AddressInterfaceFactory $addressFactory
  222. * @param \Magento\Customer\Model\Metadata\FormFactory $metadataFormFactory
  223. * @param \Magento\Customer\Api\GroupRepositoryInterface $groupRepository
  224. * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
  225. * @param EmailSender $emailSender
  226. * @param \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry
  227. * @param Item\Updater $quoteItemUpdater
  228. * @param \Magento\Framework\DataObject\Factory $objectFactory
  229. * @param \Magento\Quote\Api\CartRepositoryInterface $quoteRepository
  230. * @param \Magento\Customer\Api\AccountManagementInterface $accountManagement
  231. * @param \Magento\Customer\Api\Data\CustomerInterfaceFactory $customerFactory
  232. * @param \Magento\Customer\Model\Customer\Mapper $customerMapper
  233. * @param \Magento\Quote\Api\CartManagementInterface $quoteManagement
  234. * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper
  235. * @param \Magento\Sales\Api\OrderManagementInterface $orderManagement
  236. * @param \Magento\Quote\Model\QuoteFactory $quoteFactory
  237. * @param array $data
  238. * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer
  239. * @param ExtensibleDataObjectConverter|null $dataObjectConverter
  240. * @param StoreManagerInterface $storeManager
  241. * @SuppressWarnings(PHPMD.ExcessiveParameterList)
  242. */
  243. public function __construct(
  244. \Magento\Framework\ObjectManagerInterface $objectManager,
  245. \Magento\Framework\Event\ManagerInterface $eventManager,
  246. \Magento\Framework\Registry $coreRegistry,
  247. \Magento\Sales\Model\Config $salesConfig,
  248. \Magento\Backend\Model\Session\Quote $quoteSession,
  249. \Psr\Log\LoggerInterface $logger,
  250. \Magento\Framework\DataObject\Copy $objectCopyService,
  251. \Magento\Framework\Message\ManagerInterface $messageManager,
  252. Product\Quote\Initializer $quoteInitializer,
  253. \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository,
  254. \Magento\Customer\Api\AddressRepositoryInterface $addressRepository,
  255. \Magento\Customer\Api\Data\AddressInterfaceFactory $addressFactory,
  256. \Magento\Customer\Model\Metadata\FormFactory $metadataFormFactory,
  257. \Magento\Customer\Api\GroupRepositoryInterface $groupRepository,
  258. \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
  259. \Magento\Sales\Model\AdminOrder\EmailSender $emailSender,
  260. \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry,
  261. \Magento\Quote\Model\Quote\Item\Updater $quoteItemUpdater,
  262. \Magento\Framework\DataObject\Factory $objectFactory,
  263. \Magento\Quote\Api\CartRepositoryInterface $quoteRepository,
  264. \Magento\Customer\Api\AccountManagementInterface $accountManagement,
  265. \Magento\Customer\Api\Data\CustomerInterfaceFactory $customerFactory,
  266. \Magento\Customer\Model\Customer\Mapper $customerMapper,
  267. \Magento\Quote\Api\CartManagementInterface $quoteManagement,
  268. \Magento\Framework\Api\DataObjectHelper $dataObjectHelper,
  269. \Magento\Sales\Api\OrderManagementInterface $orderManagement,
  270. \Magento\Quote\Model\QuoteFactory $quoteFactory,
  271. array $data = [],
  272. \Magento\Framework\Serialize\Serializer\Json $serializer = null,
  273. ExtensibleDataObjectConverter $dataObjectConverter = null,
  274. StoreManagerInterface $storeManager = null
  275. ) {
  276. $this->_objectManager = $objectManager;
  277. $this->_eventManager = $eventManager;
  278. $this->_coreRegistry = $coreRegistry;
  279. $this->_salesConfig = $salesConfig;
  280. $this->_session = $quoteSession;
  281. $this->_logger = $logger;
  282. $this->_objectCopyService = $objectCopyService;
  283. $this->quoteInitializer = $quoteInitializer;
  284. $this->messageManager = $messageManager;
  285. $this->customerRepository = $customerRepository;
  286. $this->addressRepository = $addressRepository;
  287. $this->addressFactory = $addressFactory;
  288. $this->_metadataFormFactory = $metadataFormFactory;
  289. $this->customerFactory = $customerFactory;
  290. $this->groupRepository = $groupRepository;
  291. $this->_scopeConfig = $scopeConfig;
  292. $this->emailSender = $emailSender;
  293. $this->stockRegistry = $stockRegistry;
  294. $this->quoteItemUpdater = $quoteItemUpdater;
  295. $this->objectFactory = $objectFactory;
  296. $this->quoteRepository = $quoteRepository;
  297. $this->accountManagement = $accountManagement;
  298. $this->customerMapper = $customerMapper;
  299. $this->quoteManagement = $quoteManagement;
  300. $this->dataObjectHelper = $dataObjectHelper;
  301. $this->orderManagement = $orderManagement;
  302. $this->quoteFactory = $quoteFactory;
  303. $this->serializer = $serializer ?: ObjectManager::getInstance()
  304. ->get(\Magento\Framework\Serialize\Serializer\Json::class);
  305. parent::__construct($data);
  306. $this->dataObjectConverter = $dataObjectConverter ?: ObjectManager::getInstance()
  307. ->get(ExtensibleDataObjectConverter::class);
  308. $this->storeManager = $storeManager ?: ObjectManager::getInstance()->get(StoreManagerInterface::class);
  309. }
  310. /**
  311. * Set validate data in import data flag
  312. *
  313. * @param boolean $flag
  314. * @return $this
  315. */
  316. public function setIsValidate($flag)
  317. {
  318. $this->_isValidate = (bool)$flag;
  319. return $this;
  320. }
  321. /**
  322. * Return is validate data in import flag
  323. *
  324. * @return boolean
  325. * @SuppressWarnings(PHPMD.BooleanGetMethodName)
  326. */
  327. public function getIsValidate()
  328. {
  329. return $this->_isValidate;
  330. }
  331. /**
  332. * Retrieve quote item
  333. *
  334. * @param int|\Magento\Quote\Model\Quote\Item $item
  335. * @return \Magento\Quote\Model\Quote\Item|false
  336. */
  337. protected function _getQuoteItem($item)
  338. {
  339. if ($item instanceof \Magento\Quote\Model\Quote\Item) {
  340. return $item;
  341. } elseif (is_numeric($item)) {
  342. return $this->getSession()->getQuote()->getItemById($item);
  343. }
  344. return false;
  345. }
  346. /**
  347. * Initialize data for price rules
  348. *
  349. * @return $this
  350. */
  351. public function initRuleData()
  352. {
  353. $this->_coreRegistry->register(
  354. 'rule_data',
  355. new \Magento\Framework\DataObject(
  356. [
  357. 'store_id' => $this->_session->getStore()->getId(),
  358. 'website_id' => $this->_session->getStore()->getWebsiteId(),
  359. 'customer_group_id' => $this->getCustomerGroupId()
  360. ]
  361. )
  362. );
  363. return $this;
  364. }
  365. /**
  366. * Set collect totals flag for quote
  367. *
  368. * @param bool $flag
  369. * @return $this
  370. */
  371. public function setRecollect($flag)
  372. {
  373. $this->_needCollect = $flag;
  374. return $this;
  375. }
  376. /**
  377. * Recollect totals for customer cart.
  378. *
  379. * Set recollect totals flag for quote.
  380. *
  381. * @return $this
  382. */
  383. public function recollectCart()
  384. {
  385. if ($this->_needCollectCart === true) {
  386. $this->getCustomerCart()->collectTotals();
  387. $this->quoteRepository->save($this->getCustomerCart());
  388. }
  389. $this->setRecollect(true);
  390. return $this;
  391. }
  392. /**
  393. * Quote saving
  394. *
  395. * @return $this
  396. */
  397. public function saveQuote()
  398. {
  399. if (!$this->getQuote()->getId()) {
  400. return $this;
  401. }
  402. if ($this->_needCollect) {
  403. $this->getQuote()->collectTotals();
  404. }
  405. $this->quoteRepository->save($this->getQuote());
  406. return $this;
  407. }
  408. /**
  409. * Retrieve session model object of quote
  410. *
  411. * @return \Magento\Backend\Model\Session\Quote
  412. */
  413. public function getSession()
  414. {
  415. return $this->_session;
  416. }
  417. /**
  418. * Retrieve quote object model
  419. *
  420. * @return \Magento\Quote\Model\Quote
  421. */
  422. public function getQuote()
  423. {
  424. if (!$this->_quote) {
  425. $this->_quote = $this->getSession()->getQuote();
  426. }
  427. return $this->_quote;
  428. }
  429. /**
  430. * Set quote object
  431. *
  432. * @param \Magento\Quote\Model\Quote $quote
  433. * @return $this
  434. */
  435. public function setQuote(\Magento\Quote\Model\Quote $quote)
  436. {
  437. $this->_quote = $quote;
  438. return $this;
  439. }
  440. /**
  441. * Initialize creation data from existing order
  442. *
  443. * @param \Magento\Sales\Model\Order $order
  444. * @return $this
  445. * @throws \Magento\Framework\Exception\LocalizedException
  446. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  447. * @SuppressWarnings(PHPMD.NPathComplexity)
  448. */
  449. public function initFromOrder(\Magento\Sales\Model\Order $order)
  450. {
  451. $session = $this->getSession();
  452. $session->setData($order->getReordered() ? 'reordered' : 'order_id', $order->getId());
  453. $session->setCurrencyId($order->getOrderCurrencyCode());
  454. /* Check if we edit guest order */
  455. $session->setCustomerId($order->getCustomerId() ?: false);
  456. $session->setStoreId($order->getStoreId());
  457. /* Initialize catalog rule data with new session values */
  458. $this->initRuleData();
  459. foreach ($order->getItemsCollection($this->_salesConfig->getAvailableProductTypes(), true) as $orderItem) {
  460. /* @var $orderItem \Magento\Sales\Model\Order\Item */
  461. if (!$orderItem->getParentItem()) {
  462. $qty = $orderItem->getQtyOrdered();
  463. if (!$order->getReordered()) {
  464. $qty -= max($orderItem->getQtyShipped(), $orderItem->getQtyInvoiced());
  465. }
  466. if ($qty > 0) {
  467. $item = $this->initFromOrderItem($orderItem, $qty);
  468. if (is_string($item)) {
  469. throw new \Magento\Framework\Exception\LocalizedException(__($item));
  470. }
  471. }
  472. }
  473. }
  474. $shippingAddress = $order->getShippingAddress();
  475. if ($shippingAddress) {
  476. $shippingAddress->setSameAsBilling($this->isAddressesAreEqual($order));
  477. }
  478. $this->_initBillingAddressFromOrder($order);
  479. $this->_initShippingAddressFromOrder($order);
  480. $quote = $this->getQuote();
  481. if (!$quote->isVirtual() && $this->getShippingAddress()->getSameAsBilling()) {
  482. $this->setShippingAsBilling(1);
  483. }
  484. $this->setShippingMethod($order->getShippingMethod());
  485. $quote->getShippingAddress()->setShippingDescription($order->getShippingDescription());
  486. $orderCouponCode = $order->getCouponCode();
  487. if ($orderCouponCode) {
  488. $quote->setCouponCode($orderCouponCode);
  489. }
  490. if ($quote->getCouponCode()) {
  491. $quote->collectTotals();
  492. }
  493. $this->_objectCopyService->copyFieldsetToTarget('sales_copy_order', 'to_edit', $order, $quote);
  494. $this->_eventManager->dispatch('sales_convert_order_to_quote', ['order' => $order, 'quote' => $quote]);
  495. if (!$order->getCustomerId()) {
  496. $quote->setCustomerIsGuest(true);
  497. }
  498. if ($session->getUseOldShippingMethod(true)) {
  499. /*
  500. * if we are making reorder or editing old order
  501. * we need to show old shipping as preselected
  502. * so for this we need to collect shipping rates
  503. */
  504. $this->collectShippingRates();
  505. } else {
  506. /*
  507. * if we are creating new order then we don't need to collect
  508. * shipping rates before customer hit appropriate button
  509. */
  510. $this->collectRates();
  511. }
  512. $quote->getShippingAddress()->unsCachedItemsAll();
  513. $quote->setTotalsCollectedFlag(false);
  514. $this->quoteRepository->save($quote);
  515. return $this;
  516. }
  517. /**
  518. * Copy billing address from order
  519. *
  520. * @param \Magento\Sales\Model\Order $order
  521. * @return void
  522. */
  523. protected function _initBillingAddressFromOrder(\Magento\Sales\Model\Order $order)
  524. {
  525. $this->getQuote()->getBillingAddress()->setCustomerAddressId('');
  526. $this->_objectCopyService->copyFieldsetToTarget(
  527. 'sales_copy_order_billing_address',
  528. 'to_order',
  529. $order->getBillingAddress(),
  530. $this->getQuote()->getBillingAddress()
  531. );
  532. }
  533. /**
  534. * Copy shipping address from order
  535. *
  536. * @param \Magento\Sales\Model\Order $order
  537. * @return void
  538. */
  539. protected function _initShippingAddressFromOrder(\Magento\Sales\Model\Order $order)
  540. {
  541. $orderShippingAddress = $order->getShippingAddress();
  542. $quoteShippingAddress = $this->getQuote()->getShippingAddress()->setCustomerAddressId(
  543. ''
  544. )->setSameAsBilling(
  545. $orderShippingAddress && $orderShippingAddress->getSameAsBilling()
  546. );
  547. $this->_objectCopyService->copyFieldsetToTarget(
  548. 'sales_copy_order_shipping_address',
  549. 'to_order',
  550. $orderShippingAddress,
  551. $quoteShippingAddress
  552. );
  553. }
  554. /**
  555. * Initialize creation data from existing order Item
  556. *
  557. * @param \Magento\Sales\Model\Order\Item $orderItem
  558. * @param int $qty
  559. * @return \Magento\Quote\Model\Quote\Item|string|$this
  560. */
  561. public function initFromOrderItem(\Magento\Sales\Model\Order\Item $orderItem, $qty = null)
  562. {
  563. if (!$orderItem->getId()) {
  564. return $this;
  565. }
  566. $product = $this->_objectManager->create(
  567. \Magento\Catalog\Model\Product::class
  568. )->setStoreId(
  569. $this->getSession()->getStoreId()
  570. )->load(
  571. $orderItem->getProductId()
  572. );
  573. if ($product->getId()) {
  574. $product->setSkipCheckRequiredOption(true);
  575. $buyRequest = $orderItem->getBuyRequest();
  576. if (is_numeric($qty)) {
  577. $buyRequest->setQty($qty);
  578. }
  579. $item = $this->getQuote()->addProduct($product, $buyRequest);
  580. if (is_string($item)) {
  581. return $item;
  582. }
  583. if ($additionalOptions = $orderItem->getProductOptionByCode('additional_options')) {
  584. $item->addOption(
  585. new \Magento\Framework\DataObject(
  586. [
  587. 'product' => $item->getProduct(),
  588. 'code' => 'additional_options',
  589. 'value' => $this->serializer->serialize($additionalOptions)
  590. ]
  591. )
  592. );
  593. }
  594. $this->_eventManager->dispatch(
  595. 'sales_convert_order_item_to_quote_item',
  596. ['order_item' => $orderItem, 'quote_item' => $item]
  597. );
  598. return $item;
  599. }
  600. return $this;
  601. }
  602. /**
  603. * Retrieve customer wishlist model object
  604. *
  605. * @param bool $cacheReload pass cached wishlist object and get new one
  606. * @return \Magento\Wishlist\Model\Wishlist|false Return false if customer ID is not specified
  607. */
  608. public function getCustomerWishlist($cacheReload = false)
  609. {
  610. if ($this->_wishlist !== null && !$cacheReload) {
  611. return $this->_wishlist;
  612. }
  613. $customerId = (int)$this->getSession()->getCustomerId();
  614. if ($customerId) {
  615. $this->_wishlist = $this->_objectManager->create(\Magento\Wishlist\Model\Wishlist::class);
  616. $this->_wishlist->loadByCustomerId($customerId, true);
  617. $this->_wishlist->setStore(
  618. $this->getSession()->getStore()
  619. )->setSharedStoreIds(
  620. $this->getSession()->getStore()->getWebsite()->getStoreIds()
  621. );
  622. } else {
  623. $this->_wishlist = false;
  624. }
  625. return $this->_wishlist;
  626. }
  627. /**
  628. * Retrieve customer cart quote object model
  629. *
  630. * @return \Magento\Quote\Model\Quote
  631. */
  632. public function getCustomerCart()
  633. {
  634. if ($this->_cart !== null) {
  635. return $this->_cart;
  636. }
  637. $this->_cart = $this->quoteFactory->create();
  638. $customerId = (int)$this->getSession()->getCustomerId();
  639. $storeId = (int)$this->getSession()->getStoreId();
  640. if ($customerId) {
  641. try {
  642. $this->_cart = $this->quoteRepository->getForCustomer($customerId, [$storeId]);
  643. } catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
  644. $this->_cart->setStore($this->getSession()->getStore());
  645. $customerData = $this->customerRepository->getById($customerId);
  646. $this->_cart->assignCustomer($customerData);
  647. $this->quoteRepository->save($this->_cart);
  648. }
  649. }
  650. return $this->_cart;
  651. }
  652. /**
  653. * Retrieve customer compare list model object
  654. *
  655. * @return \Magento\Catalog\Model\Product\Compare\ListCompare
  656. */
  657. public function getCustomerCompareList()
  658. {
  659. if ($this->_compareList !== null) {
  660. return $this->_compareList;
  661. }
  662. $customerId = (int)$this->getSession()->getCustomerId();
  663. if ($customerId) {
  664. $this->_compareList = $this->_objectManager->create(
  665. \Magento\Catalog\Model\Product\Compare\ListCompare::class
  666. );
  667. } else {
  668. $this->_compareList = false;
  669. }
  670. return $this->_compareList;
  671. }
  672. /**
  673. * Retrieve current customer group ID.
  674. *
  675. * @return int
  676. */
  677. public function getCustomerGroupId()
  678. {
  679. $groupId = $this->getQuote()->getCustomerGroupId();
  680. if (!$groupId) {
  681. $groupId = $this->getSession()->getCustomerGroupId();
  682. }
  683. return $groupId;
  684. }
  685. /**
  686. * Move quote item to another items list
  687. *
  688. * @param int|\Magento\Quote\Model\Quote\Item $item
  689. * @param string $moveTo
  690. * @param int $qty
  691. * @return $this
  692. * @throws \Magento\Framework\Exception\LocalizedException
  693. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  694. * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
  695. */
  696. public function moveQuoteItem($item, $moveTo, $qty)
  697. {
  698. $item = $this->_getQuoteItem($item);
  699. if ($item) {
  700. $removeItem = false;
  701. $moveTo = explode('_', $moveTo);
  702. switch ($moveTo[0]) {
  703. case 'order':
  704. $info = $item->getBuyRequest();
  705. $info->setOptions($this->_prepareOptionsForRequest($item))->setQty($qty);
  706. $product = $this->_objectManager->create(
  707. \Magento\Catalog\Model\Product::class
  708. )->setStoreId(
  709. $this->getQuote()->getStoreId()
  710. )->load(
  711. $item->getProduct()->getId()
  712. );
  713. $product->setSkipCheckRequiredOption(true);
  714. $newItem = $this->getQuote()->addProduct($product, $info);
  715. if (is_string($newItem)) {
  716. throw new \Magento\Framework\Exception\LocalizedException(__($newItem));
  717. }
  718. $product->unsSkipCheckRequiredOption();
  719. $newItem->checkData();
  720. $this->_needCollectCart = true;
  721. break;
  722. case 'cart':
  723. $cart = $this->getCustomerCart();
  724. if ($cart && $item->getOptionByCode('additional_options') === null) {
  725. //options and info buy request
  726. $product = $this->_objectManager->create(
  727. \Magento\Catalog\Model\Product::class
  728. )->setStoreId(
  729. $this->getQuote()->getStoreId()
  730. )->load(
  731. $item->getProduct()->getId()
  732. );
  733. $info = $item->getOptionByCode('info_buyRequest');
  734. if ($info) {
  735. $info = new \Magento\Framework\DataObject(
  736. $this->serializer->unserialize($info->getValue())
  737. );
  738. $info->setQty($qty);
  739. $info->setOptions($this->_prepareOptionsForRequest($item));
  740. } else {
  741. $info = new \Magento\Framework\DataObject(
  742. [
  743. 'product_id' => $product->getId(),
  744. 'qty' => $qty,
  745. 'options' => $this->_prepareOptionsForRequest($item)
  746. ]
  747. );
  748. }
  749. $cartItem = $cart->addProduct($product, $info);
  750. if (is_string($cartItem)) {
  751. throw new \Magento\Framework\Exception\LocalizedException(__($cartItem));
  752. }
  753. $cartItem->setPrice($item->getProduct()->getPrice());
  754. $this->_needCollectCart = true;
  755. $removeItem = true;
  756. }
  757. break;
  758. case 'wishlist':
  759. $wishlist = null;
  760. if (!isset($moveTo[1])) {
  761. $wishlist = $this->_objectManager->create(
  762. \Magento\Wishlist\Model\Wishlist::class
  763. )->loadByCustomerId(
  764. $this->getSession()->getCustomerId(),
  765. true
  766. );
  767. } else {
  768. $wishlist = $this->_objectManager->create(
  769. \Magento\Wishlist\Model\Wishlist::class
  770. )->load($moveTo[1]);
  771. if (!$wishlist->getId() || $wishlist->getCustomerId() != $this->getSession()->getCustomerId()
  772. ) {
  773. $wishlist = null;
  774. }
  775. }
  776. if (!$wishlist) {
  777. throw new \Magento\Framework\Exception\LocalizedException(
  778. __('We can\'t find this wish list.')
  779. );
  780. }
  781. $wishlist->setStore(
  782. $this->getSession()->getStore()
  783. )->setSharedStoreIds(
  784. $this->getSession()->getStore()->getWebsite()->getStoreIds()
  785. );
  786. if ($wishlist->getId() && $item->getProduct()->isVisibleInSiteVisibility()) {
  787. $info = $item->getBuyRequest();
  788. $info->setOptions(
  789. $this->_prepareOptionsForRequest($item)
  790. )->setQty(
  791. $qty
  792. )->setStoreId(
  793. $this->getSession()->getStoreId()
  794. );
  795. $wishlist->addNewItem($item->getProduct(), $info);
  796. $removeItem = true;
  797. }
  798. break;
  799. case 'remove':
  800. $removeItem = true;
  801. break;
  802. default:
  803. break;
  804. }
  805. if ($removeItem) {
  806. $this->getQuote()->deleteItem($item);
  807. }
  808. $this->setRecollect(true);
  809. }
  810. return $this;
  811. }
  812. /**
  813. * Handle data sent from sidebar
  814. *
  815. * @param array $data
  816. * @return $this
  817. * @throws \Magento\Framework\Exception\LocalizedException
  818. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  819. * @SuppressWarnings(PHPMD.NPathComplexity)
  820. * @SuppressWarnings(PHPMD.UnusedLocalVariable)
  821. */
  822. public function applySidebarData($data)
  823. {
  824. if (isset($data['add_order_item'])) {
  825. foreach ($data['add_order_item'] as $orderItemId => $value) {
  826. /* @var $orderItem \Magento\Sales\Model\Order\Item */
  827. $orderItem = $this->_objectManager->create(\Magento\Sales\Model\Order\Item::class)->load($orderItemId);
  828. $item = $this->initFromOrderItem($orderItem);
  829. if (is_string($item)) {
  830. throw new \Magento\Framework\Exception\LocalizedException(__($item));
  831. }
  832. }
  833. }
  834. if (isset($data['add_cart_item'])) {
  835. foreach ($data['add_cart_item'] as $itemId => $qty) {
  836. $item = $this->getCustomerCart()->getItemById($itemId);
  837. if ($item) {
  838. $this->moveQuoteItem($item, 'order', $qty);
  839. $this->removeItem($itemId, 'cart');
  840. }
  841. }
  842. }
  843. if (isset($data['add_wishlist_item'])) {
  844. foreach ($data['add_wishlist_item'] as $itemId => $qty) {
  845. $item = $this->_objectManager->create(
  846. \Magento\Wishlist\Model\Item::class
  847. )->loadWithOptions(
  848. $itemId,
  849. 'info_buyRequest'
  850. );
  851. if ($item->getId()) {
  852. $this->addProduct($item->getProduct(), $item->getBuyRequest()->toArray());
  853. }
  854. }
  855. }
  856. if (isset($data['add'])) {
  857. foreach ($data['add'] as $productId => $qty) {
  858. $this->addProduct($productId, ['qty' => $qty]);
  859. }
  860. }
  861. if (isset($data['remove'])) {
  862. foreach ($data['remove'] as $itemId => $from) {
  863. $this->removeItem($itemId, $from);
  864. }
  865. }
  866. if (isset($data['empty_customer_cart']) && (int)$data['empty_customer_cart'] == 1) {
  867. $this->getCustomerCart()->removeAllItems()->collectTotals();
  868. $this->quoteRepository->save($this->getCustomerCart());
  869. }
  870. $this->recollectCart();
  871. return $this;
  872. }
  873. /**
  874. * Remove item from some of customer items storage (shopping cart, wishlist etc.)
  875. *
  876. * @param int $itemId
  877. * @param string $from
  878. * @return $this
  879. */
  880. public function removeItem($itemId, $from)
  881. {
  882. switch ($from) {
  883. case 'quote':
  884. $this->removeQuoteItem($itemId);
  885. break;
  886. case 'cart':
  887. $cart = $this->getCustomerCart();
  888. if ($cart) {
  889. $cart->removeItem($itemId);
  890. $this->_needCollectCart = true;
  891. }
  892. break;
  893. case 'wishlist':
  894. $wishlist = $this->getCustomerWishlist();
  895. if ($wishlist) {
  896. $item = $this->_objectManager->create(\Magento\Wishlist\Model\Item::class)->load($itemId);
  897. $item->delete();
  898. }
  899. break;
  900. case 'compared':
  901. $this->_objectManager->create(
  902. \Magento\Catalog\Model\Product\Compare\Item::class
  903. )->load($itemId)->delete();
  904. break;
  905. }
  906. return $this;
  907. }
  908. /**
  909. * Remove quote item
  910. *
  911. * @param int $item
  912. * @return $this
  913. */
  914. public function removeQuoteItem($item)
  915. {
  916. $this->getQuote()->removeItem($item);
  917. $this->setRecollect(true);
  918. return $this;
  919. }
  920. /**
  921. * Add product to current order quote
  922. * $product can be either product id or product model
  923. * $config can be either buyRequest config, or just qty
  924. *
  925. * @param int|\Magento\Catalog\Model\Product $product
  926. * @param array|float|int|\Magento\Framework\DataObject $config
  927. * @return $this
  928. * @throws \Magento\Framework\Exception\LocalizedException
  929. */
  930. public function addProduct($product, $config = 1)
  931. {
  932. if (!is_array($config) && !$config instanceof \Magento\Framework\DataObject) {
  933. $config = ['qty' => $config];
  934. }
  935. $config = new \Magento\Framework\DataObject($config);
  936. if (!$product instanceof \Magento\Catalog\Model\Product) {
  937. $productId = $product;
  938. $product = $this->_objectManager->create(
  939. \Magento\Catalog\Model\Product::class
  940. )->setStore(
  941. $this->getSession()->getStore()
  942. )->setStoreId(
  943. $this->getSession()->getStoreId()
  944. )->load(
  945. $product
  946. );
  947. if (!$product->getId()) {
  948. throw new \Magento\Framework\Exception\LocalizedException(
  949. __('We could not add a product to cart by the ID "%1".', $productId)
  950. );
  951. }
  952. }
  953. $item = $this->quoteInitializer->init($this->getQuote(), $product, $config);
  954. if (is_string($item)) {
  955. throw new \Magento\Framework\Exception\LocalizedException(__($item));
  956. }
  957. $item->checkData();
  958. $this->setRecollect(true);
  959. return $this;
  960. }
  961. /**
  962. * Add multiple products to current order quote
  963. *
  964. * @param array $products
  965. * @return $this
  966. */
  967. public function addProducts(array $products)
  968. {
  969. foreach ($products as $productId => $config) {
  970. $config['qty'] = isset($config['qty']) ? (double)$config['qty'] : 1;
  971. try {
  972. $this->addProduct($productId, $config);
  973. } catch (\Magento\Framework\Exception\LocalizedException $e) {
  974. $this->messageManager->addErrorMessage($e->getMessage());
  975. } catch (\Exception $e) {
  976. return $e;
  977. }
  978. }
  979. return $this;
  980. }
  981. /**
  982. * Update quantity of order quote items
  983. *
  984. * @param array $items
  985. * @return $this
  986. * @throws \Exception|\Magento\Framework\Exception\LocalizedException
  987. */
  988. public function updateQuoteItems($items)
  989. {
  990. if (!is_array($items)) {
  991. return $this;
  992. }
  993. try {
  994. foreach ($items as $itemId => $info) {
  995. if (!empty($info['configured'])) {
  996. $item = $this->getQuote()->updateItem($itemId, $this->objectFactory->create($info));
  997. $info['qty'] = (double)$item->getQty();
  998. } else {
  999. $item = $this->getQuote()->getItemById($itemId);
  1000. if (!$item) {
  1001. continue;
  1002. }
  1003. $info['qty'] = (double)$info['qty'];
  1004. }
  1005. $this->quoteItemUpdater->update($item, $info);
  1006. if ($item && !empty($info['action'])) {
  1007. $this->moveQuoteItem($item, $info['action'], $item->getQty());
  1008. }
  1009. }
  1010. } catch (\Magento\Framework\Exception\LocalizedException $e) {
  1011. $this->recollectCart();
  1012. throw $e;
  1013. } catch (\Exception $e) {
  1014. $this->_logger->critical($e);
  1015. }
  1016. $this->recollectCart();
  1017. return $this;
  1018. }
  1019. /**
  1020. * Parse additional options and sync them with product options
  1021. *
  1022. * @param \Magento\Quote\Model\Quote\Item $item
  1023. * @param string $additionalOptions
  1024. * @return array
  1025. * @throws \Magento\Framework\Exception\LocalizedException
  1026. *
  1027. * @deprecated 101.0.0
  1028. */
  1029. protected function _parseOptions(\Magento\Quote\Model\Quote\Item $item, $additionalOptions)
  1030. {
  1031. $productOptions = $this->_objectManager->get(
  1032. \Magento\Catalog\Model\Product\Option\Type\DefaultType::class
  1033. )->setProduct(
  1034. $item->getProduct()
  1035. )->getProductOptions();
  1036. $newOptions = [];
  1037. $newAdditionalOptions = [];
  1038. foreach (explode("\n", $additionalOptions) as $_additionalOption) {
  1039. if (strlen(trim($_additionalOption))) {
  1040. try {
  1041. if (strpos($_additionalOption, ':') === false) {
  1042. throw new \Magento\Framework\Exception\LocalizedException(
  1043. __('There is an error in one of the option rows.')
  1044. );
  1045. }
  1046. list($label, $value) = explode(':', $_additionalOption, 2);
  1047. } catch (\Exception $e) {
  1048. throw new \Magento\Framework\Exception\LocalizedException(
  1049. __('There is an error in one of the option rows.')
  1050. );
  1051. }
  1052. $label = trim($label);
  1053. $value = trim($value);
  1054. if (empty($value)) {
  1055. continue;
  1056. }
  1057. if (array_key_exists($label, $productOptions)) {
  1058. $optionId = $productOptions[$label]['option_id'];
  1059. $option = $item->getProduct()->getOptionById($optionId);
  1060. $group = $this->_objectManager->get(
  1061. \Magento\Catalog\Model\Product\Option::class
  1062. )->groupFactory(
  1063. $option->getType()
  1064. )->setOption(
  1065. $option
  1066. )->setProduct(
  1067. $item->getProduct()
  1068. );
  1069. $parsedValue = $group->parseOptionValue($value, $productOptions[$label]['values']);
  1070. if ($parsedValue !== null) {
  1071. $newOptions[$optionId] = $parsedValue;
  1072. } else {
  1073. $newAdditionalOptions[] = ['label' => $label, 'value' => $value];
  1074. }
  1075. } else {
  1076. $newAdditionalOptions[] = ['label' => $label, 'value' => $value];
  1077. }
  1078. }
  1079. }
  1080. return ['options' => $newOptions, 'additional_options' => $newAdditionalOptions];
  1081. }
  1082. /**
  1083. * Assign options to item
  1084. *
  1085. * @param \Magento\Quote\Model\Quote\Item $item
  1086. * @param array $options
  1087. * @return $this
  1088. *
  1089. * @deprecated 101.0.0
  1090. */
  1091. protected function _assignOptionsToItem(\Magento\Quote\Model\Quote\Item $item, $options)
  1092. {
  1093. $optionIds = $item->getOptionByCode('option_ids');
  1094. if ($optionIds) {
  1095. foreach (explode(',', $optionIds->getValue()) as $optionId) {
  1096. $item->removeOption('option_' . $optionId);
  1097. }
  1098. $item->removeOption('option_ids');
  1099. }
  1100. if ($item->getOptionByCode('additional_options')) {
  1101. $item->removeOption('additional_options');
  1102. }
  1103. $item->save();
  1104. if (!empty($options['options'])) {
  1105. $item->addOption(
  1106. new \Magento\Framework\DataObject(
  1107. [
  1108. 'product' => $item->getProduct(),
  1109. 'code' => 'option_ids',
  1110. 'value' => implode(',', array_keys($options['options']))
  1111. ]
  1112. )
  1113. );
  1114. foreach ($options['options'] as $optionId => $optionValue) {
  1115. $item->addOption(
  1116. new \Magento\Framework\DataObject(
  1117. [
  1118. 'product' => $item->getProduct(),
  1119. 'code' => 'option_' . $optionId,
  1120. 'value' => $optionValue
  1121. ]
  1122. )
  1123. );
  1124. }
  1125. }
  1126. if (!empty($options['additional_options'])) {
  1127. $item->addOption(
  1128. new \Magento\Framework\DataObject(
  1129. [
  1130. 'product' => $item->getProduct(),
  1131. 'code' => 'additional_options',
  1132. 'value' => $this->serializer->serialize($options['additional_options'])
  1133. ]
  1134. )
  1135. );
  1136. }
  1137. return $this;
  1138. }
  1139. /**
  1140. * Prepare options array for info buy request
  1141. *
  1142. * @param \Magento\Quote\Model\Quote\Item $item
  1143. * @return array
  1144. */
  1145. protected function _prepareOptionsForRequest($item)
  1146. {
  1147. $newInfoOptions = [];
  1148. $optionIds = $item->getOptionByCode('option_ids');
  1149. if ($optionIds) {
  1150. foreach (explode(',', $optionIds->getValue()) as $optionId) {
  1151. $option = $item->getProduct()->getOptionById($optionId);
  1152. $optionValue = $item->getOptionByCode('option_' . $optionId)->getValue();
  1153. $group = $this->_objectManager->get(
  1154. \Magento\Catalog\Model\Product\Option::class
  1155. )->groupFactory(
  1156. $option->getType()
  1157. )->setOption(
  1158. $option
  1159. )->setQuoteItem(
  1160. $item
  1161. );
  1162. $newInfoOptions[$optionId] = $group->prepareOptionValueForRequest($optionValue);
  1163. }
  1164. }
  1165. return $newInfoOptions;
  1166. }
  1167. /**
  1168. * Return valid price
  1169. *
  1170. * @param float|int $price
  1171. * @return float|int
  1172. */
  1173. protected function _parseCustomPrice($price)
  1174. {
  1175. $price = $this->_objectManager->get(\Magento\Framework\Locale\FormatInterface::class)->getNumber($price);
  1176. $price = $price > 0 ? $price : 0;
  1177. return $price;
  1178. }
  1179. /**
  1180. * Retrieve order quote shipping address
  1181. *
  1182. * @return \Magento\Quote\Model\Quote\Address
  1183. */
  1184. public function getShippingAddress()
  1185. {
  1186. return $this->getQuote()->getShippingAddress();
  1187. }
  1188. /**
  1189. * Return Customer (Checkout) Form instance
  1190. *
  1191. * @param \Magento\Customer\Api\Data\CustomerInterface $customer
  1192. * @return CustomerForm
  1193. */
  1194. protected function _createCustomerForm(\Magento\Customer\Api\Data\CustomerInterface $customer)
  1195. {
  1196. $customerForm = $this->_metadataFormFactory->create(
  1197. \Magento\Customer\Api\CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER,
  1198. 'adminhtml_checkout',
  1199. $this->customerMapper->toFlatArray($customer),
  1200. false,
  1201. CustomerForm::IGNORE_INVISIBLE
  1202. );
  1203. return $customerForm;
  1204. }
  1205. /**
  1206. * Set and validate Quote address
  1207. *
  1208. * All errors added to _errors
  1209. *
  1210. * @param \Magento\Quote\Model\Quote\Address $address
  1211. * @param array $data
  1212. * @return $this
  1213. */
  1214. protected function _setQuoteAddress(\Magento\Quote\Model\Quote\Address $address, array $data)
  1215. {
  1216. $isAjax = !$this->getIsValidate();
  1217. // Region is a Data Object, so it is represented by an array. validateData() doesn't understand arrays, so we
  1218. // need to merge region data with address data. This is going to be removed when we switch to use address Data
  1219. // Object instead of the address model.
  1220. // Note: if we use getRegion() here it will pull region from db using the region_id
  1221. $data = isset($data['region']) && is_array($data['region']) ? array_merge($data, $data['region']) : $data;
  1222. $addressForm = $this->_metadataFormFactory->create(
  1223. AddressMetadataInterface::ENTITY_TYPE_ADDRESS,
  1224. 'adminhtml_customer_address',
  1225. $data,
  1226. $isAjax,
  1227. CustomerForm::DONT_IGNORE_INVISIBLE,
  1228. []
  1229. );
  1230. // prepare request
  1231. // save original request structure for files
  1232. if ($address->getAddressType() == \Magento\Quote\Model\Quote\Address::TYPE_SHIPPING) {
  1233. $requestData = ['order' => ['shipping_address' => $data]];
  1234. $requestScope = 'order/shipping_address';
  1235. } else {
  1236. $requestData = ['order' => ['billing_address' => $data]];
  1237. $requestScope = 'order/billing_address';
  1238. }
  1239. $request = $addressForm->prepareRequest($requestData);
  1240. $addressData = $addressForm->extractData($request, $requestScope);
  1241. if ($this->getIsValidate()) {
  1242. $errors = $addressForm->validateData($addressData);
  1243. if ($errors !== true) {
  1244. if ($address->getAddressType() == \Magento\Quote\Model\Quote\Address::TYPE_SHIPPING) {
  1245. $typeName = __('Shipping Address: ');
  1246. } else {
  1247. $typeName = __('Billing Address: ');
  1248. }
  1249. foreach ($errors as $error) {
  1250. $this->_errors[] = $typeName . $error;
  1251. }
  1252. $address->setData($addressForm->restoreData($addressData));
  1253. } else {
  1254. $address->setData($addressForm->compactData($addressData));
  1255. }
  1256. } else {
  1257. $address->addData($addressForm->restoreData($addressData));
  1258. }
  1259. return $this;
  1260. }
  1261. /**
  1262. * Set shipping address into quote
  1263. *
  1264. * @param \Magento\Quote\Model\Quote\Address|array $address
  1265. * @return $this
  1266. */
  1267. public function setShippingAddress($address)
  1268. {
  1269. if (is_array($address)) {
  1270. $shippingAddress = $this->_objectManager->create(
  1271. \Magento\Quote\Model\Quote\Address::class
  1272. )->setData(
  1273. $address
  1274. )->setAddressType(
  1275. \Magento\Quote\Model\Quote\Address::TYPE_SHIPPING
  1276. );
  1277. if (!$this->getQuote()->isVirtual()) {
  1278. $this->_setQuoteAddress($shippingAddress, $address);
  1279. }
  1280. /**
  1281. * save_in_address_book is not a valid attribute and is filtered out by _setQuoteAddress,
  1282. * that is why it should be added after _setQuoteAddress call
  1283. */
  1284. $saveInAddressBook = (int)(!empty($address['save_in_address_book']));
  1285. $shippingAddress->setData('save_in_address_book', $saveInAddressBook);
  1286. }
  1287. if ($address instanceof \Magento\Quote\Model\Quote\Address) {
  1288. $shippingAddress = $address;
  1289. }
  1290. $this->setRecollect(true);
  1291. $this->getQuote()->setShippingAddress($shippingAddress);
  1292. return $this;
  1293. }
  1294. /**
  1295. * Set shipping address to be same as billing
  1296. *
  1297. * @param bool $flag If true - don't save in address book and actually copy data across billing and shipping
  1298. * addresses
  1299. * @return $this
  1300. */
  1301. public function setShippingAsBilling($flag)
  1302. {
  1303. if ($flag) {
  1304. $tmpAddress = clone $this->getBillingAddress();
  1305. $tmpAddress->unsAddressId()->unsAddressType();
  1306. $data = $tmpAddress->getData();
  1307. $data['save_in_address_book'] = 0;
  1308. // Do not duplicate address (billing address will do saving too)
  1309. $this->getShippingAddress()->addData($data);
  1310. }
  1311. $this->getShippingAddress()->setSameAsBilling($flag);
  1312. $this->setRecollect(true);
  1313. return $this;
  1314. }
  1315. /**
  1316. * Retrieve quote billing address
  1317. *
  1318. * @return \Magento\Quote\Model\Quote\Address
  1319. */
  1320. public function getBillingAddress()
  1321. {
  1322. return $this->getQuote()->getBillingAddress();
  1323. }
  1324. /**
  1325. * Set billing address into quote
  1326. *
  1327. * @param array $address
  1328. * @return $this
  1329. */
  1330. public function setBillingAddress($address)
  1331. {
  1332. if (!is_array($address)) {
  1333. return $this;
  1334. }
  1335. $billingAddress = $this->_objectManager->create(Address::class)
  1336. ->setData($address)
  1337. ->setAddressType(Address::TYPE_BILLING);
  1338. $this->_setQuoteAddress($billingAddress, $address);
  1339. /**
  1340. * save_in_address_book is not a valid attribute and is filtered out by _setQuoteAddress,
  1341. * that is why it should be added after _setQuoteAddress call
  1342. */
  1343. $saveInAddressBook = (int)(!empty($address['save_in_address_book']));
  1344. $billingAddress->setData('save_in_address_book', $saveInAddressBook);
  1345. $quote = $this->getQuote();
  1346. if (!$quote->isVirtual() && $this->getShippingAddress()->getSameAsBilling()) {
  1347. $address['save_in_address_book'] = 0;
  1348. $this->setShippingAddress($address);
  1349. }
  1350. // not assigned billing address should be saved as new
  1351. // but if quote already has the billing address it won't be overridden
  1352. if (empty($billingAddress->getCustomerAddressId())) {
  1353. $billingAddress->setCustomerAddressId(null);
  1354. $quote->getBillingAddress()->setCustomerAddressId(null);
  1355. }
  1356. $quote->setBillingAddress($billingAddress);
  1357. return $this;
  1358. }
  1359. /**
  1360. * Set shipping method
  1361. *
  1362. * @param string $method
  1363. * @return $this
  1364. */
  1365. public function setShippingMethod($method)
  1366. {
  1367. $this->getShippingAddress()->setShippingMethod($method);
  1368. $this->setRecollect(true);
  1369. return $this;
  1370. }
  1371. /**
  1372. * Empty shipping method and clear shipping rates
  1373. *
  1374. * @return $this
  1375. */
  1376. public function resetShippingMethod()
  1377. {
  1378. $this->getShippingAddress()->setShippingMethod(false);
  1379. $this->getShippingAddress()->removeAllShippingRates();
  1380. return $this;
  1381. }
  1382. /**
  1383. * Collect shipping data for quote shipping address
  1384. *
  1385. * @return $this
  1386. */
  1387. public function collectShippingRates()
  1388. {
  1389. $store = $this->getQuote()->getStore();
  1390. $this->storeManager->setCurrentStore($store);
  1391. $this->getQuote()->getShippingAddress()->setCollectShippingRates(true);
  1392. $this->collectRates();
  1393. return $this;
  1394. }
  1395. /**
  1396. * Calculate totals
  1397. *
  1398. * @return void
  1399. */
  1400. public function collectRates()
  1401. {
  1402. $this->getQuote()->collectTotals();
  1403. }
  1404. /**
  1405. * Set payment method into quote
  1406. *
  1407. * @param string $method
  1408. * @return $this
  1409. */
  1410. public function setPaymentMethod($method)
  1411. {
  1412. $this->getQuote()->getPayment()->setMethod($method);
  1413. return $this;
  1414. }
  1415. /**
  1416. * Set payment data into quote
  1417. *
  1418. * @param array $data
  1419. * @return $this
  1420. */
  1421. public function setPaymentData($data)
  1422. {
  1423. if (!isset($data['method'])) {
  1424. $data['method'] = $this->getQuote()->getPayment()->getMethod();
  1425. }
  1426. $this->getQuote()->getPayment()->importData($data);
  1427. return $this;
  1428. }
  1429. /**
  1430. * Add coupon code to the quote
  1431. *
  1432. * @param string $code
  1433. * @return $this
  1434. */
  1435. public function applyCoupon($code)
  1436. {
  1437. $code = trim((string)$code);
  1438. $this->getQuote()->getShippingAddress()->setCollectShippingRates(true);
  1439. if (empty($code)) {
  1440. $this->getQuote()->getShippingAddress()->setFreeShipping(0);
  1441. }
  1442. $this->getQuote()->setCouponCode($code);
  1443. $this->setRecollect(true);
  1444. return $this;
  1445. }
  1446. /**
  1447. * Add account data to quote
  1448. *
  1449. * @param array $accountData
  1450. * @return $this
  1451. */
  1452. public function setAccountData($accountData)
  1453. {
  1454. $customer = $this->getQuote()->getCustomer();
  1455. if (empty($accountData['email'])) {
  1456. $accountData['email'] = $customer->getEmail();
  1457. }
  1458. $form = $this->_createCustomerForm($customer);
  1459. // emulate request
  1460. $request = $form->prepareRequest($accountData);
  1461. $data = $form->extractData($request);
  1462. $data = $form->restoreData($data);
  1463. $customer = $this->customerFactory->create();
  1464. $this->dataObjectHelper->populateWithArray(
  1465. $customer,
  1466. $data,
  1467. \Magento\Customer\Api\Data\CustomerInterface::class
  1468. );
  1469. $this->getQuote()->updateCustomerData($customer);
  1470. $data = [];
  1471. $customerData = $this->customerMapper->toFlatArray($customer);
  1472. foreach ($form->getAttributes() as $attribute) {
  1473. $code = sprintf('customer_%s', $attribute->getAttributeCode());
  1474. $data[$code] = isset($customerData[$attribute->getAttributeCode()])
  1475. ? $customerData[$attribute->getAttributeCode()]
  1476. : null;
  1477. }
  1478. if (isset($data['customer_group_id'])) {
  1479. $customerGroup = $this->groupRepository->getById($data['customer_group_id']);
  1480. $data['customer_tax_class_id'] = $customerGroup->getTaxClassId();
  1481. $this->setRecollect(true);
  1482. }
  1483. $this->getQuote()->addData($data);
  1484. return $this;
  1485. }
  1486. /**
  1487. * Parse data retrieved from request
  1488. *
  1489. * @param array $data
  1490. * @return $this
  1491. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  1492. * @SuppressWarnings(PHPMD.NPathComplexity)
  1493. */
  1494. public function importPostData($data)
  1495. {
  1496. if (is_array($data)) {
  1497. $this->addData($data);
  1498. } else {
  1499. return $this;
  1500. }
  1501. if (isset($data['account'])) {
  1502. $this->setAccountData($data['account']);
  1503. }
  1504. if (isset($data['comment'])) {
  1505. $this->getQuote()->addData($data['comment']);
  1506. if (empty($data['comment']['customer_note_notify'])) {
  1507. $this->getQuote()->setCustomerNoteNotify(false);
  1508. } else {
  1509. $this->getQuote()->setCustomerNoteNotify(true);
  1510. }
  1511. }
  1512. if (isset($data['billing_address'])) {
  1513. $this->setBillingAddress($data['billing_address']);
  1514. }
  1515. if (isset($data['shipping_address'])) {
  1516. $this->setShippingAddress($data['shipping_address']);
  1517. }
  1518. if (isset($data['shipping_method'])) {
  1519. $this->setShippingMethod($data['shipping_method']);
  1520. }
  1521. if (isset($data['payment_method'])) {
  1522. $this->setPaymentMethod($data['payment_method']);
  1523. }
  1524. if (isset($data['coupon']['code'])) {
  1525. $this->applyCoupon($data['coupon']['code']);
  1526. }
  1527. return $this;
  1528. }
  1529. /**
  1530. * Check whether we need to create new customer (for another website) during order creation
  1531. *
  1532. * @param \Magento\Store\Model\Store $store
  1533. * @return bool
  1534. */
  1535. protected function _customerIsInStore($store)
  1536. {
  1537. $customer = $this->getQuote()->getCustomer();
  1538. return $customer->getWebsiteId() == $store->getWebsiteId()
  1539. || $this->accountManagement->isCustomerInStore($customer->getWebsiteId(), $store->getId());
  1540. }
  1541. /**
  1542. * Set and validate Customer data. Return the updated Data Object merged with the account data
  1543. *
  1544. * @param \Magento\Customer\Api\Data\CustomerInterface $customer
  1545. * @return \Magento\Customer\Api\Data\CustomerInterface
  1546. */
  1547. protected function _validateCustomerData(\Magento\Customer\Api\Data\CustomerInterface $customer)
  1548. {
  1549. $form = $this->_createCustomerForm($customer);
  1550. // emulate request
  1551. $request = $form->prepareRequest(['order' => $this->getData()]);
  1552. $data = $form->extractData($request, 'order/account');
  1553. $validationResults = $this->accountManagement->validate($customer);
  1554. if (!$validationResults->isValid()) {
  1555. $errors = $validationResults->getMessages();
  1556. if (is_array($errors)) {
  1557. foreach ($errors as $error) {
  1558. $this->_errors[] = $error;
  1559. }
  1560. }
  1561. }
  1562. $data = $form->restoreData($data);
  1563. foreach ($data as $key => $value) {
  1564. if ($value !== null) {
  1565. unset($data[$key]);
  1566. }
  1567. }
  1568. $this->dataObjectHelper->populateWithArray(
  1569. $customer,
  1570. $data,
  1571. \Magento\Customer\Api\Data\CustomerInterface::class
  1572. );
  1573. return $customer;
  1574. }
  1575. /**
  1576. * Prepare customer data for order creation.
  1577. *
  1578. * Create customer if not created using data from customer form.
  1579. * Create customer billing/shipping address if necessary using data from customer address forms.
  1580. * Set customer data to quote.
  1581. *
  1582. * @return $this
  1583. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  1584. */
  1585. public function _prepareCustomer()
  1586. {
  1587. if ($this->getQuote()->getCustomerIsGuest()) {
  1588. return $this;
  1589. }
  1590. /** @var $store \Magento\Store\Model\Store */
  1591. $store = $this->getSession()->getStore();
  1592. $customer = $this->getQuote()->getCustomer();
  1593. if ($customer->getId() && !$this->_customerIsInStore($store)) {
  1594. /** Create a new customer record if it is not available in the specified store */
  1595. /** Unset customer ID to ensure that new customer will be created */
  1596. $customer->setId(null)
  1597. ->setStoreId($store->getId())
  1598. ->setWebsiteId($store->getWebsiteId())
  1599. ->setCreatedAt(null);
  1600. $customer = $this->_validateCustomerData($customer);
  1601. } else if (!$customer->getId()) {
  1602. /** Create new customer */
  1603. $customerBillingAddressDataObject = $this->getBillingAddress()->exportCustomerAddress();
  1604. $customer->setSuffix($customerBillingAddressDataObject->getSuffix())
  1605. ->setFirstname($customerBillingAddressDataObject->getFirstname())
  1606. ->setLastname($customerBillingAddressDataObject->getLastname())
  1607. ->setMiddlename($customerBillingAddressDataObject->getMiddlename())
  1608. ->setPrefix($customerBillingAddressDataObject->getPrefix())
  1609. ->setStoreId($store->getId())
  1610. ->setWebsiteId($store->getWebsiteId())
  1611. ->setEmail($this->_getNewCustomerEmail());
  1612. $customer = $this->_validateCustomerData($customer);
  1613. }
  1614. $this->getQuote()->setCustomer($customer);
  1615. if ($this->getBillingAddress()->getSaveInAddressBook()) {
  1616. $this->_prepareCustomerAddress($this->getQuote()->getCustomer(), $this->getBillingAddress());
  1617. $address = $this->getBillingAddress()->setCustomerId($this->getQuote()->getCustomer()->getId());
  1618. $this->setBillingAddress($address);
  1619. }
  1620. if (!$this->getQuote()->isVirtual() && $this->getShippingAddress()->getSaveInAddressBook()) {
  1621. $this->_prepareCustomerAddress($this->getQuote()->getCustomer(), $this->getShippingAddress());
  1622. $address = $this->getShippingAddress()->setCustomerId($this->getQuote()->getCustomer()->getId());
  1623. $this->setShippingAddress($address);
  1624. }
  1625. $this->getBillingAddress()->setCustomerId($customer->getId());
  1626. $this->getQuote()->updateCustomerData($this->getQuote()->getCustomer());
  1627. $customer = $this->getQuote()->getCustomer();
  1628. $origAddresses = $customer->getAddresses(); // save original addresses
  1629. $customer->setAddresses([]);
  1630. $customerData = $this->customerMapper->toFlatArray($customer);
  1631. $customer->setAddresses($origAddresses); // restore original addresses
  1632. foreach ($this->_createCustomerForm($customer)->getUserAttributes() as $attribute) {
  1633. if (isset($customerData[$attribute->getAttributeCode()])) {
  1634. $quoteCode = sprintf('customer_%s', $attribute->getAttributeCode());
  1635. $this->getQuote()->setData($quoteCode, $customerData[$attribute->getAttributeCode()]);
  1636. }
  1637. }
  1638. return $this;
  1639. }
  1640. /**
  1641. * Create customer address and save it in the quote so that it can be used to persist later.
  1642. *
  1643. * @param \Magento\Customer\Api\Data\CustomerInterface $customer
  1644. * @param \Magento\Quote\Model\Quote\Address $quoteCustomerAddress
  1645. * @return void
  1646. * @throws \InvalidArgumentException
  1647. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  1648. */
  1649. protected function _prepareCustomerAddress($customer, $quoteCustomerAddress)
  1650. {
  1651. // Possible that customerId is null for new customers
  1652. $quoteCustomerAddress->setCustomerId($customer->getId());
  1653. $customerAddress = $quoteCustomerAddress->exportCustomerAddress();
  1654. $quoteAddressId = $quoteCustomerAddress->getCustomerAddressId();
  1655. $addressType = $quoteCustomerAddress->getAddressType();
  1656. if ($quoteAddressId) {
  1657. /** Update existing address */
  1658. $existingAddressDataObject = $this->addressRepository->getById($quoteAddressId);
  1659. /** Update customer address data */
  1660. $this->dataObjectHelper->mergeDataObjects(
  1661. get_class($existingAddressDataObject),
  1662. $existingAddressDataObject,
  1663. $customerAddress
  1664. );
  1665. $customerAddress = $existingAddressDataObject;
  1666. } elseif ($addressType == \Magento\Quote\Model\Quote\Address::ADDRESS_TYPE_SHIPPING) {
  1667. try {
  1668. $billingAddressDataObject = $this->accountManagement->getDefaultBillingAddress($customer->getId());
  1669. } catch (\Exception $e) {
  1670. /** Billing address does not exist. */
  1671. }
  1672. $isShippingAsBilling = $quoteCustomerAddress->getSameAsBilling();
  1673. if (isset($billingAddressDataObject) && $isShippingAsBilling) {
  1674. /** Set existing billing address as default shipping */
  1675. $customerAddress = $billingAddressDataObject;
  1676. $customerAddress->setIsDefaultShipping(true);
  1677. }
  1678. }
  1679. switch ($addressType) {
  1680. case \Magento\Quote\Model\Quote\Address::ADDRESS_TYPE_BILLING:
  1681. if ($customer->getDefaultBilling() === null) {
  1682. $customerAddress->setIsDefaultBilling(true);
  1683. }
  1684. break;
  1685. case \Magento\Quote\Model\Quote\Address::ADDRESS_TYPE_SHIPPING:
  1686. if ($customer->getDefaultShipping() === null) {
  1687. $customerAddress->setIsDefaultShipping(true);
  1688. }
  1689. break;
  1690. default:
  1691. throw new \InvalidArgumentException('Customer address type is invalid.');
  1692. }
  1693. $this->getQuote()->setCustomer($customer);
  1694. $this->getQuote()->addCustomerAddress($customerAddress);
  1695. }
  1696. /**
  1697. * Prepare item options
  1698. *
  1699. * @return $this
  1700. */
  1701. protected function _prepareQuoteItems()
  1702. {
  1703. foreach ($this->getQuote()->getAllItems() as $item) {
  1704. $options = [];
  1705. $productOptions = $item->getProduct()->getTypeInstance()->getOrderOptions($item->getProduct());
  1706. if ($productOptions) {
  1707. $productOptions['info_buyRequest']['options'] = $this->_prepareOptionsForRequest($item);
  1708. $options = $productOptions;
  1709. }
  1710. $addOptions = $item->getOptionByCode('additional_options');
  1711. if ($addOptions) {
  1712. $options['additional_options'] = $this->serializer->unserialize($addOptions->getValue());
  1713. }
  1714. $item->setProductOrderOptions($options);
  1715. }
  1716. return $this;
  1717. }
  1718. /**
  1719. * Create new order
  1720. *
  1721. * @return \Magento\Sales\Model\Order
  1722. */
  1723. public function createOrder()
  1724. {
  1725. $this->_prepareCustomer();
  1726. $this->_validate();
  1727. $quote = $this->getQuote();
  1728. $this->_prepareQuoteItems();
  1729. $orderData = [];
  1730. if ($this->getSession()->getOrder()->getId()) {
  1731. $oldOrder = $this->getSession()->getOrder();
  1732. $originalId = $oldOrder->getOriginalIncrementId();
  1733. if (!$originalId) {
  1734. $originalId = $oldOrder->getIncrementId();
  1735. }
  1736. $orderData = [
  1737. 'original_increment_id' => $originalId,
  1738. 'relation_parent_id' => $oldOrder->getId(),
  1739. 'relation_parent_real_id' => $oldOrder->getIncrementId(),
  1740. 'edit_increment' => $oldOrder->getEditIncrement() + 1,
  1741. 'increment_id' => $originalId . '-' . ($oldOrder->getEditIncrement() + 1)
  1742. ];
  1743. $quote->setReservedOrderId($orderData['increment_id']);
  1744. }
  1745. $order = $this->quoteManagement->submit($quote, $orderData);
  1746. if ($this->getSession()->getOrder()->getId()) {
  1747. $oldOrder = $this->getSession()->getOrder();
  1748. $oldOrder->setRelationChildId($order->getId());
  1749. $oldOrder->setRelationChildRealId($order->getIncrementId());
  1750. $oldOrder->save();
  1751. $this->orderManagement->cancel($oldOrder->getEntityId());
  1752. $order->save();
  1753. }
  1754. if ($this->getSendConfirmation()) {
  1755. $this->emailSender->send($order);
  1756. }
  1757. $this->_eventManager->dispatch('checkout_submit_all_after', ['order' => $order, 'quote' => $quote]);
  1758. return $order;
  1759. }
  1760. /**
  1761. * Validate quote data before order creation
  1762. *
  1763. * @return $this
  1764. * @throws \Magento\Framework\Exception\LocalizedException
  1765. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  1766. * @SuppressWarnings(PHPMD.NPathComplexity)
  1767. */
  1768. protected function _validate()
  1769. {
  1770. if (!$this->getSession()->getStore()->getId()) {
  1771. throw new \Magento\Framework\Exception\LocalizedException(__('Please select a store'));
  1772. }
  1773. $items = $this->getQuote()->getAllItems();
  1774. if (count($items) === 0) {
  1775. $this->_errors[] = __('Please specify order items.');
  1776. }
  1777. foreach ($items as $item) {
  1778. /** @var \Magento\Quote\Model\Quote\Item $item */
  1779. $messages = $item->getMessage(false);
  1780. if ($item->getHasError() && is_array($messages) && !empty($messages)) {
  1781. $this->_errors = array_merge($this->_errors, $messages);
  1782. }
  1783. }
  1784. if (!$this->getQuote()->isVirtual()) {
  1785. if (!$this->getQuote()->getShippingAddress()->getShippingMethod()) {
  1786. $this->_errors[] = __('The shipping method is missing. Select the shipping method and try again.');
  1787. }
  1788. }
  1789. if (!$this->getQuote()->getPayment()->getMethod()) {
  1790. $this->_errors[] = __("The payment method isn't selected. Enter the payment method and try again.");
  1791. } else {
  1792. $method = $this->getQuote()->getPayment()->getMethodInstance();
  1793. if (!$method->isAvailable($this->getQuote())) {
  1794. $this->_errors[] = __('This payment method is not available.');
  1795. } else {
  1796. try {
  1797. $method->validate();
  1798. } catch (\Magento\Framework\Exception\LocalizedException $e) {
  1799. $this->_errors[] = $e->getMessage();
  1800. }
  1801. }
  1802. }
  1803. if (!empty($this->_errors)) {
  1804. /** @var LoggerInterface $logger */
  1805. $logger = ObjectManager::getInstance()->get(LoggerInterface::class);
  1806. foreach ($this->_errors as $error) {
  1807. $logger->error($error);
  1808. $this->messageManager->addErrorMessage($error);
  1809. }
  1810. throw new \Magento\Framework\Exception\LocalizedException(__('Validation is failed.'));
  1811. }
  1812. return $this;
  1813. }
  1814. /**
  1815. * Retrieve new customer email.
  1816. *
  1817. * @return string
  1818. */
  1819. protected function _getNewCustomerEmail()
  1820. {
  1821. return $this->getData('account/email');
  1822. }
  1823. /**
  1824. * Checks id shipping and billing addresses are equal.
  1825. *
  1826. * @param Order $order
  1827. * @return bool
  1828. */
  1829. private function isAddressesAreEqual(Order $order)
  1830. {
  1831. $shippingAddress = $order->getShippingAddress();
  1832. $billingAddress = $order->getBillingAddress();
  1833. $shippingData = $this->dataObjectConverter->toFlatArray($shippingAddress, [], OrderAddressInterface::class);
  1834. $billingData = $this->dataObjectConverter->toFlatArray($billingAddress, [], OrderAddressInterface::class);
  1835. unset(
  1836. $shippingData['address_type'],
  1837. $shippingData['entity_id'],
  1838. $billingData['address_type'],
  1839. $billingData['entity_id']
  1840. );
  1841. return $shippingData == $billingData;
  1842. }
  1843. }