DataTest.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Tax\Test\Unit\Helper;
  7. use Magento\Framework\DataObject as MagentoObject;
  8. /**
  9. * Class DataTest
  10. *
  11. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  12. */
  13. class DataTest extends \PHPUnit\Framework\TestCase
  14. {
  15. /**
  16. * @var \Magento\Tax\Helper\Data
  17. */
  18. protected $helper;
  19. /** @var \PHPUnit_Framework_MockObject_MockObject */
  20. protected $orderTaxManagementMock;
  21. /** @var \PHPUnit_Framework_MockObject_MockObject */
  22. protected $priceCurrencyMock;
  23. /** @var \PHPUnit_Framework_MockObject_MockObject */
  24. protected $taxConfigMock;
  25. /** @var \PHPUnit_Framework_MockObject_MockObject */
  26. protected $serializer;
  27. protected function setUp()
  28. {
  29. $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
  30. $this->orderTaxManagementMock = $this->getMockBuilder(\Magento\Tax\Api\OrderTaxManagementInterface::class)
  31. ->disableOriginalConstructor()
  32. ->getMock();
  33. $this->priceCurrencyMock = $this->getMockBuilder(\Magento\Framework\Pricing\PriceCurrencyInterface::class)
  34. ->disableOriginalConstructor()
  35. ->getMock();
  36. $this->taxConfigMock = $this->getMockBuilder(\Magento\Tax\Model\Config::class)
  37. ->disableOriginalConstructor()
  38. ->getMock();
  39. $this->serializer = $this->getMockBuilder(\Magento\Framework\Serialize\Serializer\Json::class)
  40. ->disableOriginalConstructor()
  41. ->getMock();
  42. $this->serializer->expects($this->any())
  43. ->method('serialize')
  44. ->willReturnCallback(
  45. function ($value) {
  46. return json_encode($value);
  47. }
  48. );
  49. $this->serializer->expects($this->any())
  50. ->method('unserialize')
  51. ->willReturnCallback(
  52. function ($value) {
  53. return json_decode($value, true);
  54. }
  55. );
  56. $this->helper = $objectManager->getObject(
  57. \Magento\Tax\Helper\Data::class,
  58. [
  59. 'orderTaxManagement' => $this->orderTaxManagementMock,
  60. 'priceCurrency' => $this->priceCurrencyMock,
  61. 'taxConfig' => $this->taxConfigMock,
  62. 'serializer' => $this->serializer
  63. ]
  64. );
  65. }
  66. public function testGetCalculatedTaxesEmptySource()
  67. {
  68. $source = null;
  69. $this->assertEquals([], $this->helper->getCalculatedTaxes($source));
  70. }
  71. public function testGetCalculatedTaxesForOrder()
  72. {
  73. $orderId = 1;
  74. $itemCode = 'test_code';
  75. $itemAmount = 2;
  76. $itemBaseAmount = 3;
  77. $itemTitle = 'Test title';
  78. $itemPercent = 0.1;
  79. $expectedAmount = $itemAmount + 1;
  80. $expectedBaseAmount = $itemBaseAmount + 1;
  81. $orderDetailsItem = $this->getMockBuilder(\Magento\Tax\Api\Data\OrderTaxDetailsAppliedTaxInterface::class)
  82. ->disableOriginalConstructor()
  83. ->getMock();
  84. $orderDetailsItem->expects($this->once())
  85. ->method('getCode')
  86. ->willReturn($itemCode);
  87. $orderDetailsItem->expects($this->once())
  88. ->method('getAmount')
  89. ->willReturn($itemAmount);
  90. $orderDetailsItem->expects($this->once())
  91. ->method('getBaseAmount')
  92. ->willReturn($itemBaseAmount);
  93. $orderDetailsItem->expects($this->once())
  94. ->method('getTitle')
  95. ->willReturn($itemTitle);
  96. $orderDetailsItem->expects($this->once())
  97. ->method('getPercent')
  98. ->willReturn($itemPercent);
  99. $roundValues = [
  100. [$itemAmount, $expectedAmount],
  101. [$itemBaseAmount, $expectedBaseAmount],
  102. ];
  103. $this->priceCurrencyMock->expects($this->exactly(2))
  104. ->method('round')
  105. ->will($this->returnValueMap($roundValues));
  106. $appliedTaxes = [$orderDetailsItem];
  107. $orderDetails = $this->getMockBuilder(\Magento\Tax\Api\Data\OrderTaxDetailsInterface::class)
  108. ->disableOriginalConstructor()
  109. ->getMock();
  110. $orderDetails->expects($this->once())
  111. ->method('getAppliedTaxes')
  112. ->willReturn($appliedTaxes);
  113. $this->orderTaxManagementMock->expects($this->once())
  114. ->method('getOrderTaxDetails')
  115. ->with($orderId)
  116. ->willReturn($orderDetails);
  117. $orderMock = $this->getMockBuilder(\Magento\Sales\Model\Order::class)
  118. ->disableOriginalConstructor()
  119. ->getMock();
  120. $orderMock->expects($this->once())
  121. ->method('getId')
  122. ->willReturn($orderId);
  123. $result = $this->helper->getCalculatedTaxes($orderMock);
  124. $this->assertCount(1, $result);
  125. $this->assertEquals($expectedAmount, $result[0]['tax_amount']);
  126. $this->assertEquals($expectedBaseAmount, $result[0]['base_tax_amount']);
  127. $this->assertEquals($itemTitle, $result[0]['title']);
  128. $this->assertEquals($itemPercent, $result[0]['percent']);
  129. }
  130. /**
  131. * Create OrderTaxDetails mock from array of data
  132. *
  133. * @param $inputArray
  134. * @return \PHPUnit_Framework_MockObject_MockObject|\Magento\Tax\Api\Data\OrderTaxDetailsInterface
  135. * @SuppressWarnings(PHPMD.NPathComplexity)
  136. */
  137. protected function mapOrderTaxItemDetail($inputArray)
  138. {
  139. $orderTaxItemDetailsMock = $this->getMockBuilder(\Magento\Tax\Api\Data\OrderTaxDetailsInterface::class)
  140. ->getMock();
  141. $itemMocks = [];
  142. foreach ($inputArray['items'] as $orderTaxDetailsItemData) {
  143. $itemId = isset($orderTaxDetailsItemData['item_id']) ? $orderTaxDetailsItemData['item_id'] : null;
  144. $associatedItemId = isset($orderTaxDetailsItemData['associated_item_id'])
  145. ? $orderTaxDetailsItemData['associated_item_id']
  146. : null;
  147. $itemType = isset($orderTaxDetailsItemData['type']) ? $orderTaxDetailsItemData['type'] : null;
  148. $appliedTaxesData = $orderTaxDetailsItemData['applied_taxes'];
  149. $appliedTaxesMocks = [];
  150. foreach ($appliedTaxesData as $appliedTaxData) {
  151. $appliedTaxesMock = $this->getMockBuilder(
  152. \Magento\Tax\Api\Data\OrderTaxDetailsAppliedTaxInterface::class
  153. )->getMock();
  154. $appliedTaxesMock->expects($this->any())
  155. ->method('getAmount')
  156. ->will($this->returnValue($appliedTaxData['amount']));
  157. $appliedTaxesMock->expects($this->any())
  158. ->method('getBaseAmount')
  159. ->will($this->returnValue($appliedTaxData['base_amount']));
  160. $appliedTaxesMock->expects($this->any())
  161. ->method('getCode')
  162. ->will($this->returnValue($appliedTaxData['code']));
  163. $appliedTaxesMock->expects($this->any())
  164. ->method('getTitle')
  165. ->will($this->returnValue($appliedTaxData['title']));
  166. $appliedTaxesMock->expects($this->any())
  167. ->method('getPercent')
  168. ->will($this->returnValue($appliedTaxData['percent']));
  169. $appliedTaxesMocks[] = $appliedTaxesMock;
  170. }
  171. $orderTaxDetailsItemMock = $this->getMockBuilder(\Magento\Tax\Api\Data\OrderTaxDetailsItemInterface::class)
  172. ->getMock();
  173. $orderTaxDetailsItemMock->expects($this->any())
  174. ->method('getItemId')
  175. ->will($this->returnValue($itemId));
  176. $orderTaxDetailsItemMock->expects($this->any())
  177. ->method('getAssociatedItemId')
  178. ->will($this->returnValue($associatedItemId));
  179. $orderTaxDetailsItemMock->expects($this->any())
  180. ->method('getType')
  181. ->will($this->returnValue($itemType));
  182. $orderTaxDetailsItemMock->expects($this->any())
  183. ->method('getAppliedTaxes')
  184. ->will($this->returnValue($appliedTaxesMocks));
  185. $itemMocks[] = $orderTaxDetailsItemMock;
  186. }
  187. $orderTaxItemDetailsMock->expects($this->any())
  188. ->method('getItems')
  189. ->will($this->returnValue($itemMocks));
  190. return $orderTaxItemDetailsMock;
  191. }
  192. /**
  193. * @dataProvider getCalculatedTaxesForOrderItemsDataProvider
  194. */
  195. public function testGetCalculatedTaxesForOrderItems($orderData, $invoiceData, $expectedResults)
  196. {
  197. $orderId = $orderData['order_id'];
  198. $orderShippingTaxAmount = isset($orderData['shipping_tax_amount']) ? $orderData['shipping_tax_amount'] : 0;
  199. $orderTaxDetails = $orderData['order_tax_details'];
  200. /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Sales\Model\Order $orderMock */
  201. $orderMock = $this->getMockBuilder(\Magento\Sales\Model\Order::class)
  202. ->disableOriginalConstructor()
  203. ->getMock();
  204. $orderMock->expects($this->once())
  205. ->method('getId')
  206. ->willReturn($orderId);
  207. $orderMock->expects($this->once())
  208. ->method('getShippingTaxAmount')
  209. ->willReturn($orderShippingTaxAmount);
  210. $orderTaxDetailsMock = $this->mapOrderTaxItemDetail($orderTaxDetails);
  211. $this->orderTaxManagementMock->expects($this->any())
  212. ->method('getOrderTaxDetails')
  213. ->with($orderId)
  214. ->will($this->returnValue($orderTaxDetailsMock));
  215. $invoiceShippingTaxAmount =
  216. isset($invoiceData['shipping_tax_amount']) ? $invoiceData['shipping_tax_amount'] : 0;
  217. $invoiceItems = $invoiceData['invoice_items'];
  218. /** @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Sales\Model\Order\Invoice $source */
  219. $source = $this->getMockBuilder(\Magento\Sales\Model\Order\Invoice::class)
  220. ->disableOriginalConstructor()
  221. ->getMock();
  222. $source->expects($this->once())
  223. ->method('getOrder')
  224. ->willReturn($orderMock);
  225. $source->expects($this->once())
  226. ->method('getShippingTaxAmount')
  227. ->willReturn($invoiceShippingTaxAmount);
  228. $source->expects($this->once())
  229. ->method('getItems')
  230. ->willReturn($invoiceItems);
  231. $this->priceCurrencyMock->expects($this->any())
  232. ->method('round')
  233. ->will(
  234. $this->returnCallback(
  235. function ($arg) {
  236. return round($arg, 2);
  237. }
  238. )
  239. );
  240. $result = $this->helper->getCalculatedTaxes($source);
  241. foreach ($result as $index => $appliedTax) {
  242. $expectedTax = $expectedResults[$index];
  243. foreach ($appliedTax as $attr => $value) {
  244. $this->assertEquals($expectedTax[$attr], $value, "The ".$attr." of tax does not match");
  245. }
  246. }
  247. }
  248. /**
  249. * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
  250. * @return array
  251. */
  252. public function getCalculatedTaxesForOrderItemsDataProvider()
  253. {
  254. $data = [
  255. //Scenario 1: two items, one item with 0 tax
  256. 'two_items_with_one_zero_tax' => [
  257. 'order' => [
  258. 'order_id' => 1,
  259. 'shipping_tax_amount' => 0,
  260. 'order_tax_details' => [
  261. 'items' => [
  262. 'itemTax1' => [
  263. 'item_id' => 1,
  264. 'applied_taxes' => [
  265. [
  266. 'amount' => 5.0,
  267. 'base_amount' => 5.0,
  268. 'code' => 'US-CA',
  269. 'title' => 'US-CA-Sales-Tax',
  270. 'percent' => 20.0,
  271. ],
  272. ],
  273. ],
  274. ],
  275. ],
  276. ],
  277. 'invoice' => [
  278. 'invoice_items' => [
  279. 'item1' => new MagentoObject(
  280. [
  281. 'order_item' => new MagentoObject(
  282. [
  283. 'id' => 1,
  284. 'tax_amount' => 5.00,
  285. ]
  286. ),
  287. 'tax_amount' => 2.50,
  288. ]
  289. ),
  290. 'item2' => new MagentoObject(
  291. [
  292. 'order_item' => new MagentoObject(
  293. [
  294. 'id' => 2,
  295. 'tax_amount' => 0.0,
  296. ]
  297. ),
  298. 'tax_amount' => 0.0,
  299. ]
  300. ),
  301. ],
  302. ],
  303. 'expected_results' => [
  304. [
  305. 'title' => 'US-CA-Sales-Tax',
  306. 'percent' => 20.0,
  307. 'tax_amount' => 2.5,
  308. 'base_tax_amount' => 2.5,
  309. ],
  310. ],
  311. ],
  312. //Scenario 2: one item with associated weee tax
  313. 'item_with_weee_tax_partial_invoice' => [
  314. 'order' => [
  315. 'order_id' => 1,
  316. 'shipping_tax_amount' => 0,
  317. 'order_tax_details' => [
  318. 'items' => [
  319. 'itemTax1' => [
  320. 'item_id' => 1,
  321. 'applied_taxes' => [
  322. [
  323. 'amount' => 5.0,
  324. 'base_amount' => 5.0,
  325. 'code' => 'US-CA',
  326. 'title' => 'US-CA-Sales-Tax',
  327. 'percent' => 20.0,
  328. ],
  329. ],
  330. ],
  331. 'weeeTax1' => [
  332. 'associated_item_id' => 1,
  333. 'type' => 'weee',
  334. 'applied_taxes' => [
  335. [
  336. 'amount' => 3.0,
  337. 'base_amount' => 3.0,
  338. 'code' => 'US-CA',
  339. 'title' => 'US-CA-Sales-Tax',
  340. 'percent' => 20.0,
  341. ],
  342. ],
  343. ],
  344. ],
  345. ],
  346. ],
  347. 'invoice' => [
  348. 'invoice_items' => [
  349. 'item1' => new MagentoObject(
  350. [
  351. 'order_item' => new MagentoObject(
  352. [
  353. 'id' => 1,
  354. 'tax_amount' => 5.00,
  355. ]
  356. ),
  357. 'tax_amount' => 5.0,
  358. //half of weee tax is invoiced
  359. 'tax_ratio' => json_encode(['weee' => 0.5]),
  360. ]
  361. ),
  362. ],
  363. ],
  364. 'expected_results' => [
  365. [
  366. 'title' => 'US-CA-Sales-Tax',
  367. 'percent' => 20.0,
  368. 'tax_amount' => 6.5,
  369. 'base_tax_amount' => 6.5,
  370. ],
  371. ],
  372. ],
  373. //Scenario 3: one item, with both shipping and product taxes
  374. // note that 'shipping tax' is listed before 'product tax'
  375. 'one_item_with_both_shipping_and_product_taxes' => [
  376. 'order' => [
  377. 'order_id' => 1,
  378. 'shipping_tax_amount' => 2,
  379. 'order_tax_details' => [
  380. 'items' => [
  381. 'shippingTax1' => [
  382. 'item_id' => null,
  383. 'type' => 'shipping',
  384. 'applied_taxes' => [
  385. [
  386. 'amount' => 2.0,
  387. 'base_amount' => 2.0,
  388. 'code' => 'US-CA-Ship',
  389. 'title' => 'US-CA-Sales-Tax-Ship',
  390. 'percent' => 10.0,
  391. ],
  392. ],
  393. ],
  394. 'itemTax1' => [
  395. 'item_id' => 1,
  396. 'applied_taxes' => [
  397. [
  398. 'amount' => 5.0,
  399. 'base_amount' => 5.0,
  400. 'code' => 'US-CA',
  401. 'title' => 'US-CA-Sales-Tax',
  402. 'percent' => 20.0,
  403. ],
  404. ],
  405. ],
  406. ],
  407. ],
  408. ],
  409. 'invoice' => [
  410. 'shipping_tax_amount' => 2,
  411. 'invoice_items' => [
  412. 'item1' => new MagentoObject(
  413. [
  414. 'order_item' => new MagentoObject(
  415. [
  416. 'id' => 1,
  417. 'tax_amount' => 5.00,
  418. ]
  419. ),
  420. 'tax_amount' => 5.00,
  421. ]
  422. ),
  423. ],
  424. ],
  425. // note that 'shipping tax' is now listed after 'product tax'
  426. 'expected_results' => [
  427. [
  428. 'title' => 'US-CA-Sales-Tax',
  429. 'percent' => 20.0,
  430. 'tax_amount' => 5.00,
  431. 'base_tax_amount' => 5.00,
  432. ],
  433. [
  434. 'title' => 'US-CA-Sales-Tax-Ship',
  435. 'percent' => 10.0,
  436. 'tax_amount' => 2.00,
  437. 'base_tax_amount' => 2.00,
  438. ],
  439. ],
  440. ],
  441. ];
  442. return $data;
  443. }
  444. /**
  445. * @param bool $expected
  446. * @param bool $displayBothPrices
  447. * @param bool $priceIncludesTax
  448. * @param bool $isCrossBorderTradeEnabled
  449. * @param bool $displayPriceIncludingTax
  450. * @dataProvider dataProviderIsCatalogPriceDisplayAffectedByTax
  451. */
  452. public function testIsCatalogPriceDisplayAffectedByTax(
  453. $expected,
  454. $displayBothPrices,
  455. $priceIncludesTax,
  456. $isCrossBorderTradeEnabled,
  457. $displayPriceIncludingTax
  458. ) {
  459. if ($displayBothPrices == true) {
  460. $this->taxConfigMock->expects($this->at(0))
  461. ->method('getPriceDisplayType')
  462. ->willReturn(3);
  463. } else {
  464. $this->taxConfigMock->expects($this->at(0))
  465. ->method('getPriceDisplayType')
  466. ->willReturn(2);
  467. $this->taxConfigMock->expects($this->any())
  468. ->method('priceIncludesTax')
  469. ->willReturn($priceIncludesTax);
  470. $this->taxConfigMock->expects($this->any())
  471. ->method('crossBorderTradeEnabled')
  472. ->willReturn($isCrossBorderTradeEnabled);
  473. if ($displayPriceIncludingTax == true) {
  474. $this->taxConfigMock->expects($this->at(3))
  475. ->method('getPriceDisplayType')
  476. ->willReturn(2);
  477. } else {
  478. $this->taxConfigMock->expects($this->at(2))
  479. ->method('getPriceDisplayType')
  480. ->willReturn(1);
  481. }
  482. }
  483. $this->assertSame($expected, $this->helper->isCatalogPriceDisplayAffectedByTax(null));
  484. }
  485. /**
  486. * @return array
  487. */
  488. public function dataProviderIsCatalogPriceDisplayAffectedByTax()
  489. {
  490. return [
  491. [true , true, false, false, false],
  492. [true , false, true, true, false],
  493. [true , false, true, false, true],
  494. [false , false, true, true, true],
  495. [true , false, false, true, true],
  496. [false , false, false, true, false]
  497. ];
  498. }
  499. }