CreateTest.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Sales\Controller\Adminhtml\Order;
  7. use Magento\Customer\Api\CustomerRepositoryInterface;
  8. use Magento\Backend\Model\Session\Quote as SessionQuote;
  9. use Magento\Customer\Api\Data\CustomerInterface;
  10. use Magento\Framework\Api\SearchCriteriaBuilder;
  11. use Magento\Framework\App\Config\MutableScopeConfigInterface;
  12. use Magento\Framework\Exception\NoSuchEntityException;
  13. use Magento\Quote\Api\CartRepositoryInterface;
  14. use Magento\Framework\App\Request\Http as HttpRequest;
  15. use Magento\Quote\Model\Quote;
  16. use Magento\Store\Api\Data\StoreInterface;
  17. use Magento\Store\Api\Data\WebsiteInterface;
  18. use Magento\Store\Api\StoreRepositoryInterface;
  19. use Magento\Store\Api\WebsiteRepositoryInterface;
  20. use Magento\Store\Model\ScopeInterface;
  21. /**
  22. * @magentoAppArea adminhtml
  23. * @magentoDbIsolation enabled
  24. *
  25. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  26. * @SuppressWarnings(PHPMD.TooManyPublicMethods)
  27. */
  28. class CreateTest extends \Magento\TestFramework\TestCase\AbstractBackendController
  29. {
  30. /**
  31. * @var \Magento\Catalog\Api\ProductRepositoryInterface
  32. */
  33. protected $productRepository;
  34. /**
  35. * @inheritDoc
  36. *
  37. * @throws \Magento\Framework\Exception\AuthenticationException
  38. */
  39. protected function setUp()
  40. {
  41. parent::setUp();
  42. $this->productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
  43. ->get(\Magento\Catalog\Api\ProductRepositoryInterface::class);
  44. }
  45. /**
  46. * Test LoadBlock being dispatched.
  47. */
  48. public function testLoadBlockAction()
  49. {
  50. $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
  51. $this->getRequest()->setParam('block', ',');
  52. $this->getRequest()->setParam('json', 1);
  53. $this->dispatch('backend/sales/order_create/loadBlock');
  54. $this->assertEquals('{"message":""}', $this->getResponse()->getBody());
  55. }
  56. /**
  57. * @magentoDataFixture Magento/Catalog/_files/product_simple.php
  58. */
  59. public function testLoadBlockActionData()
  60. {
  61. $product = $this->productRepository->get('simple');
  62. $this->_objectManager->get(
  63. \Magento\Sales\Model\AdminOrder\Create::class
  64. )->addProducts(
  65. [$product->getId() => ['qty' => 1]]
  66. );
  67. $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
  68. $this->getRequest()->setParam('block', 'data');
  69. $this->getRequest()->setParam('json', 1);
  70. $this->dispatch('backend/sales/order_create/loadBlock');
  71. $html = $this->getResponse()->getBody();
  72. $this->assertContains('<div id=\"sales_order_create_search_grid\"', $html);
  73. $this->assertContains('<div id=\"order-billing_method_form\"', $html);
  74. $this->assertContains('id=\"shipping-method-overlay\"', $html);
  75. $this->assertContains('id=\"coupons:code\"', $html);
  76. }
  77. /**
  78. * Tests that shipping method 'Table rates' shows rates according to selected website.
  79. *
  80. * @magentoAppArea adminhtml
  81. * @magentoDataFixture Magento/Quote/Fixtures/quote_sec_website.php
  82. * @magentoDataFixture Magento/OfflineShipping/_files/tablerates_second_website.php
  83. * @magentoDbIsolation disabled
  84. */
  85. public function testLoadBlockShippingMethod()
  86. {
  87. $store = $this->getStore('fixture_second_store');
  88. /** @var MutableScopeConfigInterface $mutableScopeConfig */
  89. $mutableScopeConfig = $this->_objectManager->get(MutableScopeConfigInterface::class);
  90. $mutableScopeConfig->setValue(
  91. 'carriers/tablerate/active',
  92. 1,
  93. ScopeInterface::SCOPE_STORE,
  94. $store->getCode()
  95. );
  96. $mutableScopeConfig->setValue(
  97. 'carriers/tablerate/condition_name',
  98. 'package_qty',
  99. ScopeInterface::SCOPE_STORE,
  100. $store->getCode()
  101. );
  102. $website = $this->getWebsite('test');
  103. $customer = $this->getCustomer('customer.web@example.com', (int)$website->getId());
  104. $quote = $this->getQuoteById('0000032134');
  105. $session = $this->_objectManager->get(SessionQuote::class);
  106. $session->setQuoteId($quote->getId());
  107. $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
  108. $this->getRequest()->setPostValue(
  109. [
  110. 'customer_id' => $customer->getId(),
  111. 'collect_shipping_rates' => 1,
  112. 'store_id' => $store->getId(),
  113. 'json' => true
  114. ]
  115. );
  116. $this->dispatch('backend/sales/order_create/loadBlock/block/shipping_method');
  117. $body = $this->getResponse()->getBody();
  118. $expectedTableRatePrice = '<span class=\"price\">$20.00<\/span>';
  119. $this->assertContains($expectedTableRatePrice, $body, '');
  120. }
  121. /**
  122. * Tests LoadBlock actions.
  123. *
  124. * @param string $block Block name.
  125. * @param string $expected Contains HTML.
  126. *
  127. * @dataProvider loadBlockActionsDataProvider
  128. */
  129. public function testLoadBlockActions($block, $expected)
  130. {
  131. $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
  132. $this->getRequest()->setParam('block', $block);
  133. $this->getRequest()->setParam('json', 1);
  134. $this->dispatch('backend/sales/order_create/loadBlock');
  135. $html = $this->getResponse()->getBody();
  136. $this->assertContains($expected, $html);
  137. }
  138. /**
  139. * @return array
  140. */
  141. public function loadBlockActionsDataProvider()
  142. {
  143. return [
  144. 'shipping_method' => ['shipping_method', 'id=\"shipping-method-overlay\"'],
  145. 'billing_method' => ['billing_method', '<div id=\"order-billing_method_form\">'],
  146. 'newsletter' => ['newsletter', 'name=\"newsletter:subscribe\"'],
  147. 'search' => ['search', '<div id=\"sales_order_create_search_grid\"'],
  148. 'search_grid' => ['search', '<div id=\"sales_order_create_search_grid\"']
  149. ];
  150. }
  151. /**
  152. * Tests action items.
  153. *
  154. * @magentoDataFixture Magento/Catalog/_files/product_simple.php
  155. */
  156. public function testLoadBlockActionItems()
  157. {
  158. $product = $this->productRepository->get('simple');
  159. $this->_objectManager->get(
  160. \Magento\Sales\Model\AdminOrder\Create::class
  161. )->addProducts(
  162. [$product->getId() => ['qty' => 1]]
  163. );
  164. $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
  165. $this->getRequest()->setParam('block', 'items');
  166. $this->getRequest()->setParam('json', 1);
  167. $this->dispatch('backend/sales/order_create/loadBlock');
  168. $html = $this->getResponse()->getBody();
  169. $this->assertContains('id=\"coupons:code\"', $html);
  170. }
  171. /**
  172. * @magentoDataFixture Magento/Catalog/_files/product_simple.php
  173. * @magentoAppArea adminhtml
  174. */
  175. public function testIndexAction()
  176. {
  177. $product = $this->productRepository->get('simple');
  178. /** @var $order \Magento\Sales\Model\AdminOrder\Create */
  179. $order = $this->_objectManager->get(\Magento\Sales\Model\AdminOrder\Create::class);
  180. $order->addProducts([$product->getId() => ['qty' => 1]]);
  181. $this->dispatch('backend/sales/order_create/index');
  182. $html = $this->getResponse()->getBody();
  183. $this->assertGreaterThanOrEqual(
  184. 1,
  185. \Magento\TestFramework\Helper\Xpath::getElementsCountForXpath(
  186. '//div[@id="order-customer-selector"]',
  187. $html
  188. )
  189. );
  190. $this->assertGreaterThanOrEqual(
  191. 1,
  192. \Magento\TestFramework\Helper\Xpath::getElementsCountForXpath(
  193. '//*[@data-grid-id="sales_order_create_customer_grid"]',
  194. $html
  195. )
  196. );
  197. $this->assertGreaterThanOrEqual(
  198. 1,
  199. \Magento\TestFramework\Helper\Xpath::getElementsCountForXpath(
  200. '//div[@id="order-billing_method_form"]',
  201. $html
  202. )
  203. );
  204. $this->assertGreaterThanOrEqual(
  205. 1,
  206. \Magento\TestFramework\Helper\Xpath::getElementsCountForXpath(
  207. '//*[@id="shipping-method-overlay"]',
  208. $html
  209. )
  210. );
  211. $this->assertGreaterThanOrEqual(
  212. 1,
  213. \Magento\TestFramework\Helper\Xpath::getElementsCountForXpath(
  214. '//div[@id="sales_order_create_search_grid"]',
  215. $html
  216. )
  217. );
  218. $this->assertGreaterThanOrEqual(
  219. 1,
  220. \Magento\TestFramework\Helper\Xpath::getElementsCountForXpath('//*[@id="coupons:code"]', $html)
  221. );
  222. }
  223. /**
  224. * Tests ACL.
  225. *
  226. * @param string $actionName
  227. * @param boolean $reordered
  228. * @param string $expectedResult
  229. *
  230. * @dataProvider getAclResourceDataProvider
  231. * @magentoAppIsolation enabled
  232. */
  233. public function testGetAclResource($actionName, $reordered, $expectedResult)
  234. {
  235. $this->_objectManager->get(SessionQuote::class)->setReordered($reordered);
  236. $orderController = $this->_objectManager->get(
  237. \Magento\Sales\Controller\Adminhtml\Order\Stub\OrderCreateStub::class
  238. );
  239. $this->getRequest()->setActionName($actionName);
  240. $method = new \ReflectionMethod(\Magento\Sales\Controller\Adminhtml\Order\Create::class, '_getAclResource');
  241. $method->setAccessible(true);
  242. $result = $method->invoke($orderController);
  243. $this->assertEquals($result, $expectedResult);
  244. }
  245. /**
  246. * @return array
  247. */
  248. public function getAclResourceDataProvider()
  249. {
  250. return [
  251. ['index', false, 'Magento_Sales::create'],
  252. ['index', true, 'Magento_Sales::reorder'],
  253. ['save', false, 'Magento_Sales::create'],
  254. ['save', true, 'Magento_Sales::reorder'],
  255. ['reorder', false, 'Magento_Sales::reorder'],
  256. ['reorder', true, 'Magento_Sales::reorder'],
  257. ['cancel', false, 'Magento_Sales::cancel'],
  258. ['cancel', true, 'Magento_Sales::reorder'],
  259. ['', false, 'Magento_Sales::actions'],
  260. ['', true, 'Magento_Sales::actions']
  261. ];
  262. }
  263. /**
  264. * @magentoDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
  265. * @magentoAppArea adminhtml
  266. */
  267. public function testConfigureProductToAddAction()
  268. {
  269. $product = $this->productRepository->get('configurable');
  270. $this->getRequest()->setParam('id', $product->getEntityId())
  271. ->setParam('isAjax', true);
  272. $this->dispatch('backend/sales/order_create/configureProductToAdd');
  273. $body = $this->getResponse()->getBody();
  274. $this->assertNotEmpty($body);
  275. $this->assertContains('><span>Quantity</span></label>', $body);
  276. $this->assertContains('>Test Configurable</label>', $body);
  277. $this->assertContains('"code":"test_configurable","label":"Test Configurable"', $body);
  278. $this->assertContains(sprintf('"productId":"%s"', $product->getEntityId()), $body);
  279. }
  280. /**
  281. * Test not allowing to save.
  282. *
  283. * @throws \Magento\Framework\Exception\LocalizedException
  284. */
  285. public function testDeniedSaveAction()
  286. {
  287. $this->_objectManager->configure(
  288. [\Magento\Backend\App\Action\Context::class => [
  289. 'arguments' => [
  290. 'authorization' => [
  291. 'instance' => \Magento\Sales\Controller\Adminhtml\Order\AuthorizationMock::class,
  292. ],
  293. ],
  294. ],
  295. ]
  296. );
  297. \Magento\TestFramework\Helper\Bootstrap::getInstance()
  298. ->loadArea('adminhtml');
  299. $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
  300. $this->dispatch('backend/sales/order_create/save');
  301. $this->assertEquals('403', $this->getResponse()->getHttpResponseCode());
  302. }
  303. /**
  304. * Checks a case when shipping is the same as billing and billing address details was changed by request.
  305. * Both billing and shipping addresses should be updated.
  306. *
  307. * @magentoAppArea adminhtml
  308. * @magentoDataFixture Magento/Sales/_files/quote_with_customer.php
  309. */
  310. public function testSyncBetweenQuoteAddresses()
  311. {
  312. /** @var CustomerRepositoryInterface $customerRepository */
  313. $customerRepository = $this->_objectManager->get(CustomerRepositoryInterface::class);
  314. $customer = $customerRepository->get('customer@example.com');
  315. /** @var CartRepositoryInterface $quoteRepository */
  316. $quoteRepository = $this->_objectManager->get(CartRepositoryInterface::class);
  317. $quote = $quoteRepository->getActiveForCustomer($customer->getId());
  318. $session = $this->_objectManager->get(SessionQuote::class);
  319. $session->setQuoteId($quote->getId());
  320. $data = [
  321. 'firstname' => 'John',
  322. 'lastname' => 'Doe',
  323. 'street' => ['Soborna 23'],
  324. 'city' => 'Kyiv',
  325. 'country_id' => 'UA',
  326. 'region' => 'Kyivska',
  327. 'region_id' => 1
  328. ];
  329. $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
  330. $this->getRequest()->setPostValue(
  331. [
  332. 'order' => ['billing_address' => $data],
  333. 'reset_shipping' => 1,
  334. 'customer_id' => $customer->getId(),
  335. 'store_id' => 1,
  336. 'json' => true
  337. ]
  338. );
  339. $this->dispatch('backend/sales/order_create/loadBlock/block/shipping_address');
  340. self::assertEquals(200, $this->getResponse()->getHttpResponseCode());
  341. $updatedQuote = $quoteRepository->get($quote->getId());
  342. $billingAddress = $updatedQuote->getBillingAddress();
  343. self::assertEquals($data['region_id'], $billingAddress->getRegionId());
  344. self::assertEquals($data['country_id'], $billingAddress->getCountryId());
  345. $shippingAddress = $updatedQuote->getShippingAddress();
  346. self::assertEquals($data['city'], $shippingAddress->getCity());
  347. self::assertEquals($data['street'], $shippingAddress->getStreet());
  348. }
  349. /**
  350. * Gets quote entity by reserved order id.
  351. *
  352. * @param string $reservedOrderId
  353. * @return Quote
  354. */
  355. private function getQuoteById(string $reservedOrderId): Quote
  356. {
  357. /** @var SearchCriteriaBuilder $searchCriteriaBuilder */
  358. $searchCriteriaBuilder = $this->_objectManager->get(SearchCriteriaBuilder::class);
  359. $searchCriteria = $searchCriteriaBuilder->addFilter('reserved_order_id', $reservedOrderId)
  360. ->create();
  361. /** @var CartRepositoryInterface $repository */
  362. $repository = $this->_objectManager->get(CartRepositoryInterface::class);
  363. $items = $repository->getList($searchCriteria)
  364. ->getItems();
  365. return array_pop($items);
  366. }
  367. /**
  368. * Gets website entity.
  369. *
  370. * @param string $code
  371. * @return WebsiteInterface
  372. * @throws NoSuchEntityException
  373. */
  374. private function getWebsite(string $code): WebsiteInterface
  375. {
  376. /** @var WebsiteRepositoryInterface $repository */
  377. $repository = $this->_objectManager->get(WebsiteRepositoryInterface::class);
  378. return $repository->get($code);
  379. }
  380. /**
  381. * Gets customer entity.
  382. *
  383. * @param string $email
  384. * @param int $websiteId
  385. * @return CustomerInterface
  386. * @throws NoSuchEntityException
  387. * @throws \Magento\Framework\Exception\LocalizedException
  388. */
  389. private function getCustomer(string $email, int $websiteId): CustomerInterface
  390. {
  391. /** @var CustomerRepositoryInterface $repository */
  392. $repository = $this->_objectManager->get(CustomerRepositoryInterface::class);
  393. return $repository->get($email, $websiteId);
  394. }
  395. /**
  396. * Gets store by code.
  397. *
  398. * @param string $code
  399. * @return StoreInterface
  400. * @throws \Magento\Framework\Exception\NoSuchEntityException
  401. */
  402. private function getStore(string $code): StoreInterface
  403. {
  404. /** @var StoreRepositoryInterface $repository */
  405. $repository = $this->_objectManager->get(StoreRepositoryInterface::class);
  406. return $repository->get($code);
  407. }
  408. }