Skip to content

Commit 51fbc83

Browse files
committed
MAGETWO-94808: Item row total display incorrect value in API response
1 parent 88e5cda commit 51fbc83

File tree

10 files changed

+343
-13
lines changed

10 files changed

+343
-13
lines changed

app/code/Magento/Sales/Block/Order/Item/Renderer/DefaultRenderer.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,4 +278,21 @@ public function getItemRowTotalAfterDiscountHtml($item = null)
278278
$block->setItem($item);
279279
return $block->toHtml();
280280
}
281+
282+
/**
283+
* Return the base total amount minus discount.
284+
*
285+
* @param OrderItem|InvoiceItem|CreditmemoItem $item
286+
* @return float|null
287+
*/
288+
public function getBaseTotalAmount($item)
289+
{
290+
$baseTotalAmount = $item->getBaseRowTotal()
291+
+ $item->getBaseTaxAmount()
292+
+ $item->getBaseDiscountTaxCompensationAmount()
293+
+ $item->getBaseWeeeTaxAppliedAmount()
294+
- $item->getBaseDiscountAmount();
295+
296+
return $baseTotalAmount;
297+
}
281298
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Sales\Model\Order\Webapi;
9+
10+
use Magento\Sales\Api\Data\OrderItemInterface;
11+
use Magento\Sales\Block\Adminhtml\Items\Column\DefaultColumn;
12+
use Magento\Sales\Block\Order\Item\Renderer\DefaultRenderer;
13+
14+
/**
15+
* Class for changing row total in response.
16+
*/
17+
class ChangeOutputArray
18+
{
19+
/**
20+
* @var DefaultColumn
21+
*/
22+
private $priceRenderer;
23+
24+
/**
25+
* @var DefaultRenderer
26+
*/
27+
private $defaultRenderer;
28+
29+
/**
30+
* @param DefaultColumn $priceRenderer
31+
* @param DefaultRenderer $defaultRenderer
32+
*/
33+
public function __construct(
34+
DefaultColumn $priceRenderer,
35+
DefaultRenderer $defaultRenderer
36+
) {
37+
$this->priceRenderer = $priceRenderer;
38+
$this->defaultRenderer = $defaultRenderer;
39+
}
40+
41+
/**
42+
* Changing row total for webapi order item response.
43+
*
44+
* @param OrderItemInterface $dataObject
45+
* @param array $result
46+
* @return array
47+
*/
48+
public function execute(
49+
OrderItemInterface $dataObject,
50+
array $result
51+
): array {
52+
$result[OrderItemInterface::ROW_TOTAL] = $this->priceRenderer->getTotalAmount($dataObject);
53+
$result[OrderItemInterface::BASE_ROW_TOTAL] = $this->priceRenderer->getBaseTotalAmount($dataObject);
54+
$result[OrderItemInterface::ROW_TOTAL_INCL_TAX] = $this->defaultRenderer->getTotalAmount($dataObject);
55+
$result[OrderItemInterface::BASE_ROW_TOTAL_INCL_TAX] = $this->defaultRenderer->getBaseTotalAmount($dataObject);
56+
57+
return $result;
58+
}
59+
}

app/code/Magento/Sales/Test/Unit/Block/Order/Item/Renderer/DefaultRendererTest.php

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -60,18 +60,7 @@ protected function setUp()
6060
->setMethods(['setItem', 'toHtml'])
6161
->getMock();
6262

63-
$itemMockMethods = [
64-
'__wakeup',
65-
'getRowTotal',
66-
'getTaxAmount',
67-
'getDiscountAmount',
68-
'getDiscountTaxCompensationAmount',
69-
'getWeeeTaxAppliedRowAmount',
70-
];
71-
$this->itemMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Item::class)
72-
->disableOriginalConstructor()
73-
->setMethods($itemMockMethods)
74-
->getMock();
63+
$this->itemMock = $this->createMock(\Magento\Sales\Model\Order\Item::class);
7564
}
7665

7766
public function testGetItemPriceHtml()
@@ -161,4 +150,20 @@ public function testGetTotalAmount()
161150

162151
$this->assertEquals($expectedResult, $this->block->getTotalAmount($this->itemMock));
163152
}
153+
154+
/**
155+
* @return void
156+
*/
157+
public function testGetBaseTotalAmount()
158+
{
159+
$expectedBaseTotalAmount = 10;
160+
161+
$this->itemMock->expects($this->once())->method('getBaseRowTotal')->willReturn(8);
162+
$this->itemMock->expects($this->once())->method('getBaseTaxAmount')->willReturn(1);
163+
$this->itemMock->expects($this->once())->method('getBaseDiscountTaxCompensationAmount')->willReturn(1);
164+
$this->itemMock->expects($this->once())->method('getBaseWeeeTaxAppliedAmount')->willReturn(1);
165+
$this->itemMock->expects($this->once())->method('getBaseDiscountAmount')->willReturn(1);
166+
167+
$this->assertEquals($expectedBaseTotalAmount, $this->block->getBaseTotalAmount($this->itemMock));
168+
}
164169
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Sales\Test\Unit\Model\Order\Webapi;
9+
10+
use Magento\Sales\Api\Data\OrderItemInterface;
11+
use Magento\Sales\Block\Adminhtml\Items\Column\DefaultColumn;
12+
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
13+
use Magento\Sales\Block\Order\Item\Renderer\DefaultRenderer;
14+
use Magento\Sales\Model\Order\Webapi\ChangeOutputArray;
15+
16+
/**
17+
* Test for Magento\Sales\Model\Order\Webapi\ChangeOutputArray class.
18+
*/
19+
class ChangeOutputArrayTest extends \PHPUnit\Framework\TestCase
20+
{
21+
/**
22+
* @var DefaultColumn|\PHPUnit_Framework_MockObject_MockObject
23+
*/
24+
private $priceRendererMock;
25+
26+
/**
27+
* @var DefaultRenderer|\PHPUnit_Framework_MockObject_MockObject
28+
*/
29+
private $defaultRendererMock;
30+
31+
/**
32+
* @var ObjectManager
33+
*/
34+
private $objectManager;
35+
36+
/**
37+
* @var ChangeOutputArray
38+
*/
39+
private $model;
40+
41+
/**
42+
* @inheritdoc
43+
*/
44+
protected function setUp()
45+
{
46+
$this->objectManager = new ObjectManager($this);
47+
48+
$this->priceRendererMock = $this->createMock(DefaultColumn::class);
49+
$this->defaultRendererMock = $this->createMock(DefaultRenderer::class);
50+
51+
$this->model = $this->objectManager->getObject(
52+
ChangeOutputArray::class,
53+
[
54+
'priceRenderer' => $this->priceRendererMock,
55+
'defaultRenderer' => $this->defaultRendererMock,
56+
]
57+
);
58+
}
59+
60+
/**
61+
* @return void
62+
*/
63+
public function testExecute()
64+
{
65+
$expectedResult = [
66+
OrderItemInterface::ROW_TOTAL => 10,
67+
OrderItemInterface::BASE_ROW_TOTAL => 10,
68+
OrderItemInterface::ROW_TOTAL_INCL_TAX => 11,
69+
OrderItemInterface::BASE_ROW_TOTAL_INCL_TAX => 11,
70+
];
71+
$orderItemInterfaceMock = $this->createMock(OrderItemInterface::class);
72+
73+
$this->priceRendererMock->expects($this->once())
74+
->method('getTotalAmount')
75+
->with($orderItemInterfaceMock)
76+
->willReturn(10);
77+
$this->priceRendererMock->expects($this->once())
78+
->method('getBaseTotalAmount')
79+
->with($orderItemInterfaceMock)
80+
->willReturn(10);
81+
$this->defaultRendererMock->expects($this->once())
82+
->method('getTotalAmount')
83+
->with($orderItemInterfaceMock)
84+
->willReturn(11);
85+
$this->defaultRendererMock->expects($this->once())
86+
->method('getBaseTotalAmount')
87+
->with($orderItemInterfaceMock)
88+
->willReturn(11);
89+
90+
$this->assertEquals($expectedResult, $this->model->execute($orderItemInterfaceMock, []));
91+
}
92+
}

app/code/Magento/Sales/etc/webapi_rest/di.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,11 @@
1515
<type name="Magento\Sales\Api\ShipmentRepositoryInterface">
1616
<plugin name="convert_blob_to_string" type="Magento\Sales\Plugin\ShippingLabelConverter" />
1717
</type>
18+
<type name="Magento\Framework\Reflection\DataObjectProcessor">
19+
<arguments>
20+
<argument name="processors" xsi:type="array">
21+
<item name="\Magento\Sales\Model\Order\Item" xsi:type="object">Magento\Sales\Model\Order\Webapi\ChangeOutputArray\Proxy</item>
22+
</argument>
23+
</arguments>
24+
</type>
1825
</config>

app/code/Magento/Sales/etc/webapi_soap/di.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,11 @@
1515
<type name="Magento\Sales\Api\ShipmentRepositoryInterface">
1616
<plugin name="convert_blob_to_string" type="Magento\Sales\Plugin\ShippingLabelConverter" />
1717
</type>
18+
<type name="Magento\Framework\Reflection\DataObjectProcessor">
19+
<arguments>
20+
<argument name="processors" xsi:type="array">
21+
<item name="\Magento\Sales\Model\Order\Item" xsi:type="object">Magento\Sales\Model\Order\Webapi\ChangeOutputArray\Proxy</item>
22+
</argument>
23+
</arguments>
24+
</type>
1825
</config>

dev/tests/api-functional/testsuite/Magento/Sales/Service/V1/OrderItemGetTest.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,37 @@ protected function assertOrderItem(\Magento\Sales\Model\Order\Item $orderItem, a
7777
$this->assertEquals($orderItem->getBasePrice(), $response['base_price']);
7878
$this->assertEquals($orderItem->getRowTotal(), $response['row_total']);
7979
}
80+
81+
/**
82+
* @return void
83+
* @magentoApiDataFixture Magento/Sales/_files/order_with_discount.php
84+
*/
85+
public function testGetOrderWithDiscount()
86+
{
87+
/** @var \Magento\Sales\Model\Order $order */
88+
$order = $this->objectManager->create(\Magento\Sales\Model\Order::class);
89+
$order->loadByIncrementId(self::ORDER_INCREMENT_ID);
90+
/** @var \Magento\Sales\Model\Order\Item $orderItem */
91+
$orderItem = current($order->getItems());
92+
93+
$serviceInfo = [
94+
'rest' => [
95+
'resourcePath' => self::RESOURCE_PATH . '/' . $orderItem->getId(),
96+
'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET,
97+
],
98+
'soap' => [
99+
'service' => self::SERVICE_NAME,
100+
'serviceVersion' => self::SERVICE_VERSION,
101+
'operation' => self::SERVICE_NAME . 'get',
102+
],
103+
];
104+
105+
$response = $this->_webApiCall($serviceInfo, ['id' => $orderItem->getId()]);
106+
107+
$this->assertTrue(is_array($response));
108+
$this->assertEquals(8.00, $response['row_total']);
109+
$this->assertEquals(8.00, $response['base_row_total']);
110+
$this->assertEquals(9.00, $response['row_total_incl_tax']);
111+
$this->assertEquals(9.00, $response['base_row_total_incl_tax']);
112+
}
80113
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
use Magento\Sales\Api\OrderRepositoryInterface;
9+
use Magento\Sales\Model\Order;
10+
use Magento\Sales\Model\Order\Address as OrderAddress;
11+
use Magento\Sales\Model\Order\Item as OrderItem;
12+
use Magento\Sales\Model\Order\Payment;
13+
use Magento\Store\Model\StoreManagerInterface;
14+
15+
require __DIR__ . '/../../../Magento/Catalog/_files/product_simple.php';
16+
/** @var \Magento\Catalog\Model\Product $product */
17+
18+
$addressData = include __DIR__ . '/address_data.php';
19+
20+
$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
21+
22+
$billingAddress = $objectManager->create(OrderAddress::class, ['data' => $addressData]);
23+
$billingAddress->setAddressType('billing');
24+
25+
$shippingAddress = clone $billingAddress;
26+
$shippingAddress->setId(null)->setAddressType('shipping');
27+
28+
/** @var Payment $payment */
29+
$payment = $objectManager->create(Payment::class);
30+
$payment->setMethod('checkmo')
31+
->setAdditionalInformation('last_trans_id', '11122')
32+
->setAdditionalInformation(
33+
'metadata',
34+
[
35+
'type' => 'free',
36+
'fraudulent' => false,
37+
]
38+
);
39+
40+
/** @var OrderItem $orderItem */
41+
$orderItem = $objectManager->create(OrderItem::class);
42+
$orderItem->setProductId($product->getId())
43+
->setQtyOrdered(2)
44+
->setBasePrice($product->getPrice())
45+
->setPrice($product->getPrice())
46+
->setRowTotal($product->getPrice())
47+
->setProductType('simple')
48+
->setDiscountAmount(2)
49+
->setBaseRowTotal($product->getPrice())
50+
->setBaseDiscountAmount(2)
51+
->setTaxAmount(1)
52+
->setBaseTaxAmount(1);
53+
54+
/** @var Order $order */
55+
$order = $objectManager->create(Order::class);
56+
$order->setIncrementId('100000001')
57+
->setState(Order::STATE_PROCESSING)
58+
->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_PROCESSING))
59+
->setSubtotal(100)
60+
->setGrandTotal(100)
61+
->setBaseSubtotal(100)
62+
->setBaseGrandTotal(100)
63+
->setCustomerIsGuest(true)
64+
->setCustomerEmail('customer@null.com')
65+
->setBillingAddress($billingAddress)
66+
->setShippingAddress($shippingAddress)
67+
->setStoreId($objectManager->get(StoreManagerInterface::class)->getStore()->getId())
68+
->addItem($orderItem)
69+
->setPayment($payment);
70+
71+
/** @var OrderRepositoryInterface $orderRepository */
72+
$orderRepository = $objectManager->create(OrderRepositoryInterface::class);
73+
$orderRepository->save($order);
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
require 'default_rollback.php';

0 commit comments

Comments
 (0)