Skip to content

Commit 427f9bf

Browse files
authored
Merge pull request #6492 from magento-tsg/2.4-develop-pr118
[Arrows] Fixes for 2.4 (pr118) (2.4-develop)
2 parents 960a51f + 18f3416 commit 427f9bf

File tree

25 files changed

+1201
-99
lines changed

25 files changed

+1201
-99
lines changed

app/code/Magento/Bundle/Model/Sales/Order/Pdf/Items/Invoice.php

Lines changed: 107 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
namespace Magento\Bundle\Model\Sales\Order\Pdf\Items;
99

1010
use Magento\Framework\Data\Collection\AbstractDb;
11+
use Magento\Framework\DataObject;
1112
use Magento\Framework\Filesystem;
1213
use Magento\Framework\Filter\FilterManager;
1314
use Magento\Framework\Model\Context;
@@ -69,50 +70,52 @@ public function __construct(
6970
}
7071

7172
/**
72-
* Draw item line
73+
* Draw bundle product item line
7374
*
7475
* @return void
75-
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
76-
* @SuppressWarnings(PHPMD.NPathComplexity)
77-
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
7876
*/
7977
public function draw()
8078
{
81-
$order = $this->getOrder();
82-
$item = $this->getItem();
83-
$pdf = $this->getPdf();
84-
$page = $this->getPage();
79+
$draw = $this->drawChildrenItems();
80+
$draw = $this->drawCustomOptions($draw);
8581

82+
$page = $this->getPdf()->drawLineBlocks($this->getPage(), $draw, ['table_header' => true]);
83+
84+
$this->setPage($page);
85+
}
86+
87+
/**
88+
* Draw bundle product children items
89+
*
90+
* @return array
91+
*/
92+
private function drawChildrenItems(): array
93+
{
8694
$this->_setFontRegular();
87-
$items = $this->getChildren($item);
8895

8996
$prevOptionId = '';
9097
$drawItems = [];
91-
92-
foreach ($items as $childItem) {
93-
$line = [];
94-
98+
$optionId = 0;
99+
$lines = [];
100+
foreach ($this->getChildren($this->getItem()) as $childItem) {
101+
$index = array_key_last($lines) !== null ? array_key_last($lines) + 1 : 0;
95102
$attributes = $this->getSelectionAttributes($childItem);
96103
if (is_array($attributes)) {
97104
$optionId = $attributes['option_id'];
98-
} else {
99-
$optionId = 0;
100105
}
101106

102107
if (!isset($drawItems[$optionId])) {
103108
$drawItems[$optionId] = ['lines' => [], 'height' => 15];
104109
}
105110

106111
if ($childItem->getOrderItem()->getParentItem() && $prevOptionId != $attributes['option_id']) {
107-
$line[0] = [
112+
$lines[$index][] = [
108113
'font' => 'italic',
109114
'text' => $this->string->split($attributes['option_label'], 45, true, true),
110115
'feed' => 35,
111116
];
112117

113-
$drawItems[$optionId] = ['lines' => [$line], 'height' => 15];
114-
115-
$line = [];
118+
$index++;
116119
$prevOptionId = $attributes['option_id'];
117120
}
118121

@@ -124,35 +127,97 @@ public function draw()
124127
$feed = 35;
125128
$name = $childItem->getName();
126129
}
127-
$line[] = ['text' => $this->string->split($name, 35, true, true), 'feed' => $feed];
130+
$lines[$index][] = ['text' => $this->string->split($name, 35, true, true), 'feed' => $feed];
128131

129-
// draw SKUs
130-
if (!$childItem->getOrderItem()->getParentItem()) {
131-
$text = [];
132-
foreach ($this->string->split($item->getSku(), 17) as $part) {
133-
$text[] = $part;
134-
}
135-
$line[] = ['text' => $text, 'feed' => 255];
136-
}
132+
$lines = $this->drawSkus($childItem, $lines);
137133

138-
// draw prices
139-
if ($this->canShowPriceInfo($childItem)) {
140-
$price = $order->formatPriceTxt($childItem->getPrice());
141-
$line[] = ['text' => $price, 'feed' => 395, 'font' => 'bold', 'align' => 'right'];
142-
$line[] = ['text' => $childItem->getQty() * 1, 'feed' => 435, 'font' => 'bold'];
134+
$lines = $this->drawPrices($childItem, $lines);
135+
}
136+
$drawItems[$optionId]['lines'] = $lines;
143137

144-
$tax = $order->formatPriceTxt($childItem->getTaxAmount());
145-
$line[] = ['text' => $tax, 'feed' => 495, 'font' => 'bold', 'align' => 'right'];
138+
return $drawItems;
139+
}
146140

147-
$row_total = $order->formatPriceTxt($childItem->getRowTotal());
148-
$line[] = ['text' => $row_total, 'feed' => 565, 'font' => 'bold', 'align' => 'right'];
141+
/**
142+
* Draw sku parts
143+
*
144+
* @param DataObject $childItem
145+
* @param array $lines
146+
* @return array
147+
*/
148+
private function drawSkus(DataObject $childItem, array $lines): array
149+
{
150+
$index = array_key_last($lines);
151+
if (!$childItem->getOrderItem()->getParentItem()) {
152+
$text = [];
153+
foreach ($this->string->split($this->getItem()->getSku(), 17) as $part) {
154+
$text[] = $part;
149155
}
156+
$lines[$index][] = ['text' => $text, 'feed' => 255];
157+
}
158+
159+
return $lines;
160+
}
150161

151-
$drawItems[$optionId]['lines'][] = $line;
162+
/**
163+
* Draw prices for bundle product children items
164+
*
165+
* @param DataObject $childItem
166+
* @param array $lines
167+
* @return array
168+
*/
169+
private function drawPrices(DataObject $childItem, array $lines): array
170+
{
171+
$index = array_key_last($lines);
172+
if ($this->canShowPriceInfo($childItem)) {
173+
$lines[$index][] = ['text' => $childItem->getQty() * 1, 'feed' => 435, 'align' => 'right'];
174+
175+
$tax = $this->getOrder()->formatPriceTxt($childItem->getTaxAmount());
176+
$lines[$index][] = ['text' => $tax, 'feed' => 495, 'font' => 'bold', 'align' => 'right'];
177+
178+
$item = $this->getItem();
179+
$this->_item = $childItem;
180+
$feedPrice = 380;
181+
$feedSubtotal = $feedPrice + 185;
182+
foreach ($this->getItemPricesForDisplay() as $priceData) {
183+
if (isset($priceData['label'])) {
184+
// draw Price label
185+
$lines[$index][] = ['text' => $priceData['label'], 'feed' => $feedPrice, 'align' => 'right'];
186+
// draw Subtotal label
187+
$lines[$index][] = ['text' => $priceData['label'], 'feed' => $feedSubtotal, 'align' => 'right'];
188+
$index++;
189+
}
190+
// draw Price
191+
$lines[$index][] = [
192+
'text' => $priceData['price'],
193+
'feed' => $feedPrice,
194+
'font' => 'bold',
195+
'align' => 'right',
196+
];
197+
// draw Subtotal
198+
$lines[$index][] = [
199+
'text' => $priceData['subtotal'],
200+
'feed' => $feedSubtotal,
201+
'font' => 'bold',
202+
'align' => 'right',
203+
];
204+
$index++;
205+
}
206+
$this->_item = $item;
152207
}
153208

154-
// custom options
155-
$options = $item->getOrderItem()->getProductOptions();
209+
return $lines;
210+
}
211+
212+
/**
213+
* Draw bundle product custom options
214+
*
215+
* @param array $draw
216+
* @return array
217+
*/
218+
private function drawCustomOptions(array $draw): array
219+
{
220+
$options = $this->getItem()->getOrderItem()->getProductOptions();
156221
if ($options && isset($options['options'])) {
157222
foreach ($options['options'] as $option) {
158223
$lines = [];
@@ -180,12 +245,10 @@ public function draw()
180245
$lines[][] = ['text' => $text, 'feed' => 40];
181246
}
182247

183-
$drawItems[] = ['lines' => $lines, 'height' => 15];
248+
$draw[] = ['lines' => $lines, 'height' => 15];
184249
}
185250
}
186251

187-
$page = $pdf->drawLineBlocks($page, $drawItems, ['table_header' => true]);
188-
189-
$this->setPage($page);
252+
return $draw;
190253
}
191254
}
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
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\Bundle\Test\Unit\Model\Sales\Order\Pdf\Items;
9+
10+
use Magento\Bundle\Model\Sales\Order\Pdf\Items\Invoice;
11+
use Magento\Framework\Data\Collection\AbstractDb;
12+
use Magento\Framework\DataObject;
13+
use Magento\Framework\Filesystem;
14+
use Magento\Framework\Filter\FilterManager;
15+
use Magento\Framework\Model\Context;
16+
use Magento\Framework\Model\ResourceModel\AbstractResource;
17+
use Magento\Framework\Registry;
18+
use Magento\Framework\Serialize\Serializer\Json;
19+
use Magento\Framework\Stdlib\StringUtils;
20+
use Magento\Sales\Model\Order;
21+
use Magento\Sales\Model\Order\Pdf\Invoice as InvoicePdf;
22+
use Magento\Tax\Helper\Data;
23+
use PHPUnit\Framework\MockObject\MockObject;
24+
use PHPUnit\Framework\TestCase;
25+
use Zend_Pdf_Page;
26+
27+
/**
28+
* Covers bundle order item invoice print logic
29+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
30+
*/
31+
class InvoiceTest extends TestCase
32+
{
33+
/**
34+
* @var Invoice|MockObject
35+
*/
36+
private $model;
37+
38+
/**
39+
* @var Data|MockObject
40+
*/
41+
private $taxDataMock;
42+
43+
/**
44+
* @inheritdoc
45+
*/
46+
protected function setUp(): void
47+
{
48+
$contextMock = $this->createMock(Context::class);
49+
$registryMock = $this->createMock(Registry::class);
50+
$this->taxDataMock = $this->createMock(Data::class);
51+
$directoryMock = $this->createMock(Filesystem\Directory\Read::class);
52+
$directoryMock->expects($this->any())->method('getAbsolutePath')->willReturn('');
53+
$filesystemMock = $this->createMock(Filesystem::class);
54+
$filesystemMock->expects($this->any())->method('getDirectoryRead')->willReturn($directoryMock);
55+
$filterManagerMock = $this->createMock(FilterManager::class);
56+
$stringUtilsMock = $this->createMock(StringUtils::class);
57+
$stringUtilsMock->expects($this->any())->method('split')->willReturnArgument(0);
58+
$resourceMock = $this->createMock(AbstractResource::class);
59+
$collectionMock = $this->createMock(AbstractDb::class);
60+
$serializerMock = $this->createMock(Json::class);
61+
62+
$this->model = $this->getMockBuilder(Invoice::class)
63+
->setConstructorArgs(
64+
[
65+
$contextMock,
66+
$registryMock,
67+
$this->taxDataMock,
68+
$filesystemMock,
69+
$filterManagerMock,
70+
$stringUtilsMock,
71+
$serializerMock,
72+
$resourceMock,
73+
$collectionMock,
74+
[],
75+
]
76+
)
77+
->onlyMethods(
78+
[
79+
'_setFontRegular',
80+
'getChildren',
81+
'isShipmentSeparately',
82+
'isChildCalculated',
83+
'getValueHtml',
84+
'getSelectionAttributes',
85+
]
86+
)
87+
->getMock();
88+
}
89+
90+
/**
91+
* @dataProvider \Magento\Bundle\Test\Unit\Model\Sales\Order\Pdf\Items\InvoiceTestProvider::getData
92+
* @param array $expected
93+
* @param string $method
94+
*/
95+
public function testDrawPrice(array $expected, string $method): void
96+
{
97+
$this->taxDataMock->expects($this->any())->method($method)->willReturn(true);
98+
$pageMock = $this->createMock(Zend_Pdf_Page::class);
99+
$this->model->setPage($pageMock);
100+
$pdfMock = $this->createMock(InvoicePdf::class);
101+
$pdfMock->expects($this->any())->method('drawLineBlocks')->with(
102+
$pageMock,
103+
$expected,
104+
['table_header' => true]
105+
)->willReturn($pageMock);
106+
$this->model->setPdf($pdfMock);
107+
108+
$this->prepareModel();
109+
$this->model->draw();
110+
}
111+
112+
/**
113+
* Prepare invoice draw model for test execution
114+
*
115+
* @return void
116+
*/
117+
private function prepareModel(): void
118+
{
119+
$parentItem = new DataObject(
120+
[
121+
'sku' => 'bundle-simple',
122+
'name' => 'Bundle',
123+
'order_item' => new DataObject(
124+
[
125+
'product_options' => [],
126+
]
127+
),
128+
]
129+
);
130+
$items = [
131+
new DataObject(
132+
[
133+
'name' => 'Simple1',
134+
'sku' => 'simple1',
135+
'price' => '10.00',
136+
'price_incl_tax' => '10.83',
137+
'row_total' => '20.00',
138+
'row_total_incl_tax' => '21.66',
139+
'qty' => '2',
140+
'tax_amount' => '1.66',
141+
'order_item' => new DataObject(
142+
[
143+
'parent_item' => $parentItem,
144+
]
145+
),
146+
]
147+
),
148+
new DataObject(
149+
[
150+
'name' => 'Simple2',
151+
'sku' => 'simple2',
152+
'price' => '5.00',
153+
'price_incl_tax' => '5.41',
154+
'row_total' => '10.00',
155+
'row_total_incl_tax' => '10.83',
156+
'qty' => '2',
157+
'tax_amount' => '0.83',
158+
'order_item' => new DataObject(
159+
[
160+
'parent_item' => $parentItem,
161+
]
162+
),
163+
]
164+
),
165+
];
166+
$orderMock = $this->createMock(Order::class);
167+
168+
$this->model->expects($this->any())->method('getChildren')->willReturn($items);
169+
$this->model->expects($this->any())->method('isShipmentSeparately')->willReturn(false);
170+
$this->model->expects($this->any())->method('isChildCalculated')->willReturn(true);
171+
$this->model->expects($this->at(2))->method('getSelectionAttributes')->willReturn(
172+
['option_id' => 1, 'option_label' => 'test option']
173+
);
174+
$this->model->expects($this->at(3))->method('getValueHtml')->willReturn($items[0]->getName());
175+
$this->model->expects($this->at(5))->method('getSelectionAttributes')->willReturn(
176+
['option_id' => 1, 'option_label' => 'second option']
177+
);
178+
$this->model->expects($this->at(6))->method('getValueHtml')->willReturn($items[1]->getName());
179+
180+
$orderMock->expects($this->any())->method('formatPriceTxt')->willReturnArgument(0);
181+
$this->model->setOrder($orderMock);
182+
$this->model->setItem($parentItem);
183+
}
184+
}

0 commit comments

Comments
 (0)