3
3
* Copyright © Magento, Inc. All rights reserved.
4
4
* See COPYING.txt for license details.
5
5
*/
6
+ declare (strict_types=1 );
7
+
6
8
namespace Magento \Sales \Model \Service ;
7
9
10
+ use Magento \Framework \Exception \LocalizedException ;
11
+ use Magento \Framework \Serialize \Serializer \Json as JsonSerializer ;
12
+ use Magento \Sales \Api \Data \InvoiceInterface ;
13
+ use Magento \Sales \Api \Data \InvoiceItemInterface ;
14
+ use Magento \Sales \Api \Data \OrderItemInterface ;
8
15
use Magento \Sales \Api \InvoiceManagementInterface ;
9
16
use Magento \Sales \Model \Order ;
10
- use Magento \Framework \App \ObjectManager ;
11
- use Magento \Framework \Serialize \Serializer \Json ;
12
- use Magento \Catalog \Model \Product \Type ;
17
+ use Magento \Sales \Model \Order \Invoice ;
13
18
14
19
/**
15
20
* Class InvoiceService
19
24
class InvoiceService implements InvoiceManagementInterface
20
25
{
21
26
/**
22
- * Repository
23
- *
24
27
* @var \Magento\Sales\Api\InvoiceRepositoryInterface
25
28
*/
26
29
protected $ repository ;
27
30
28
31
/**
29
- * Repository
30
- *
31
32
* @var \Magento\Sales\Api\InvoiceCommentRepositoryInterface
32
33
*/
33
34
protected $ commentRepository ;
34
35
35
36
/**
36
- * Search Criteria Builder
37
- *
38
37
* @var \Magento\Framework\Api\SearchCriteriaBuilder
39
38
*/
40
39
protected $ criteriaBuilder ;
41
40
42
41
/**
43
- * Filter Builder
44
- *
45
42
* @var \Magento\Framework\Api\FilterBuilder
46
43
*/
47
44
protected $ filterBuilder ;
48
45
49
46
/**
50
- * Invoice Notifier
51
- *
52
47
* @var \Magento\Sales\Model\Order\InvoiceNotifier
53
48
*/
54
49
protected $ invoiceNotifier ;
@@ -64,23 +59,19 @@ class InvoiceService implements InvoiceManagementInterface
64
59
protected $ orderConverter ;
65
60
66
61
/**
67
- * Serializer interface instance.
68
- *
69
- * @var Json
62
+ * @var JsonSerializer
70
63
*/
71
64
private $ serializer ;
72
65
73
66
/**
74
- * Constructor
75
- *
76
67
* @param \Magento\Sales\Api\InvoiceRepositoryInterface $repository
77
68
* @param \Magento\Sales\Api\InvoiceCommentRepositoryInterface $commentRepository
78
69
* @param \Magento\Framework\Api\SearchCriteriaBuilder $criteriaBuilder
79
70
* @param \Magento\Framework\Api\FilterBuilder $filterBuilder
80
71
* @param \Magento\Sales\Model\Order\InvoiceNotifier $notifier
81
72
* @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository
82
73
* @param \Magento\Sales\Model\Convert\Order $orderConverter
83
- * @param Json|null $serializer
74
+ * @param JsonSerializer $serializer
84
75
*/
85
76
public function __construct (
86
77
\Magento \Sales \Api \InvoiceRepositoryInterface $ repository ,
@@ -90,7 +81,7 @@ public function __construct(
90
81
\Magento \Sales \Model \Order \InvoiceNotifier $ notifier ,
91
82
\Magento \Sales \Api \OrderRepositoryInterface $ orderRepository ,
92
83
\Magento \Sales \Model \Convert \Order $ orderConverter ,
93
- Json $ serializer = null
84
+ JsonSerializer $ serializer
94
85
) {
95
86
$ this ->repository = $ repository ;
96
87
$ this ->commentRepository = $ commentRepository ;
@@ -99,7 +90,7 @@ public function __construct(
99
90
$ this ->invoiceNotifier = $ notifier ;
100
91
$ this ->orderRepository = $ orderRepository ;
101
92
$ this ->orderConverter = $ orderConverter ;
102
- $ this ->serializer = $ serializer ?: ObjectManager:: getInstance ()-> get (Json::class) ;
93
+ $ this ->serializer = $ serializer ;
103
94
}
104
95
105
96
/**
@@ -140,133 +131,120 @@ public function setVoid($id)
140
131
}
141
132
142
133
/**
143
- * Creates an invoice based on the order and quantities provided
134
+ * Creates an invoice based on the order and quantities provided.
135
+ *
136
+ * Explanation for `if` statements:
137
+ * - using qty defined in `$preparedItemsQty` is prioritized
138
+ * - if qty is not defined and item is dummy, get ordered qty
139
+ * - if qty is not defined, get qty to invoice
140
+ * - else qty is 0
144
141
*
145
142
* @param Order $order
146
- * @param array $qtys
147
- * @return \Magento\Sales\Model\Order\Invoice
148
- * @throws \Magento\Framework\Exception\LocalizedException
143
+ * @param array $orderItemsQtyToInvoice
144
+ * @return Invoice
145
+ * @throws LocalizedException
146
+ * @throws \Exception
149
147
*/
150
- public function prepareInvoice (Order $ order , array $ qtys = [])
151
- {
152
- $ isQtysEmpty = empty ( $ qtys );
153
- $ invoice = $ this -> orderConverter -> toInvoice ( $ order );
148
+ public function prepareInvoice (
149
+ Order $ order ,
150
+ array $ orderItemsQtyToInvoice = []
151
+ ): InvoiceInterface {
154
152
$ totalQty = 0 ;
155
- $ qtys = $ this ->prepareItemsQty ($ order , $ qtys );
153
+ $ invoice = $ this ->orderConverter ->toInvoice ($ order );
154
+ $ preparedItemsQty = $ this ->prepareItemsQty ($ order , $ orderItemsQtyToInvoice );
155
+
156
156
foreach ($ order ->getAllItems () as $ orderItem ) {
157
- if (!$ this ->_canInvoiceItem ($ orderItem , $ qtys )) {
157
+ if (!$ this ->canInvoiceItem ($ orderItem , $ preparedItemsQty )) {
158
158
continue ;
159
159
}
160
- $ item = $ this -> orderConverter -> itemToInvoiceItem ( $ orderItem );
161
- if (isset ($ qtys [$ orderItem ->getId ()])) {
162
- $ qty = ( double ) $ qtys [$ orderItem ->getId ()];
160
+
161
+ if (isset ($ preparedItemsQty [$ orderItem ->getId ()])) {
162
+ $ qty = $ preparedItemsQty [$ orderItem ->getId ()];
163
163
} elseif ($ orderItem ->isDummy ()) {
164
164
$ qty = $ orderItem ->getQtyOrdered () ? $ orderItem ->getQtyOrdered () : 1 ;
165
- } elseif ($ isQtysEmpty ) {
165
+ } elseif (empty ( $ orderItemsQtyToInvoice ) ) {
166
166
$ qty = $ orderItem ->getQtyToInvoice ();
167
167
} else {
168
168
$ qty = 0 ;
169
169
}
170
+
171
+ $ invoiceItem = $ this ->orderConverter ->itemToInvoiceItem ($ orderItem );
172
+ $ this ->setInvoiceItemQuantity ($ invoiceItem , (float ) $ qty );
173
+ $ invoice ->addItem ($ invoiceItem );
170
174
$ totalQty += $ qty ;
171
- $ this ->setInvoiceItemQuantity ($ item , $ qty );
172
- $ invoice ->addItem ($ item );
173
175
}
176
+
174
177
$ invoice ->setTotalQty ($ totalQty );
175
178
$ invoice ->collectTotals ();
176
179
$ order ->getInvoiceCollection ()->addItem ($ invoice );
180
+
177
181
return $ invoice ;
178
182
}
179
183
180
184
/**
181
- * Prepare qty to invoice for parent and child products if theirs qty is not specified in initial request.
185
+ * Prepare qty to invoice for parent and child products
186
+ * if theirs qty is not specified in initial request.
182
187
*
183
188
* @param Order $order
184
- * @param array $qtys
189
+ * @param array $orderItemsQtyToInvoice
185
190
* @return array
186
191
*/
187
- private function prepareItemsQty (Order $ order , array $ qtys = [])
188
- {
192
+ private function prepareItemsQty (
193
+ Order $ order ,
194
+ array $ orderItemsQtyToInvoice
195
+ ): array {
189
196
foreach ($ order ->getAllItems () as $ orderItem ) {
190
- if (empty ($ qtys [$ orderItem ->getId ()])) {
191
- if ($ orderItem ->getProductType () == Type::TYPE_BUNDLE && !$ orderItem ->isShipSeparately ()) {
192
- $ qtys [$ orderItem ->getId ()] = $ orderItem ->getQtyOrdered () - $ orderItem ->getQtyInvoiced ();
193
- } else {
194
- $ parentItem = $ orderItem ->getParentItem ();
195
- $ parentItemId = $ parentItem ? $ parentItem ->getId () : null ;
196
- if ($ parentItemId && isset ($ qtys [$ parentItemId ])) {
197
- $ qtys [$ orderItem ->getId ()] = $ qtys [$ parentItemId ];
198
- }
199
- continue ;
197
+ if (isset ($ orderItemsQtyToInvoice [$ orderItem ->getId ()])) {
198
+ if ($ orderItem ->isDummy () && $ orderItem ->getHasChildren ()) {
199
+ $ orderItemsQtyToInvoice = $ this ->setChildItemsQtyToInvoice ($ orderItem , $ orderItemsQtyToInvoice );
200
+ }
201
+ } else {
202
+ if (isset ($ orderItemsQtyToInvoice [$ orderItem ->getParentItemId ()])) {
203
+ $ orderItemsQtyToInvoice [$ orderItem ->getId ()] = $ orderItemsQtyToInvoice [$ orderItem ->getParentItemId ()];
200
204
}
201
205
}
202
-
203
- $ this ->prepareItemQty ($ orderItem , $ qtys );
204
206
}
205
207
206
- return $ qtys ;
208
+ return $ orderItemsQtyToInvoice ;
207
209
}
208
210
209
211
/**
210
- * Prepare qty_invoiced for order item
212
+ * Sets qty to invoice for children order items, if not set.
211
213
*
212
- * @param \Magento\Sales\Api\Data\OrderItemInterface $orderItem
213
- * @param array $qtys
214
+ * @param OrderItemInterface $parentOrderItem
215
+ * @param array $orderItemsQtyToInvoice
216
+ * @return array
214
217
*/
215
- private function prepareItemQty (\Magento \Sales \Api \Data \OrderItemInterface $ orderItem , &$ qtys )
216
- {
217
- $ this ->prepareBundleQty ($ orderItem , $ qtys );
218
+ private function setChildItemsQtyToInvoice (
219
+ OrderItemInterface $ parentOrderItem ,
220
+ array $ orderItemsQtyToInvoice
221
+ ): array {
222
+ /** @var OrderItemInterface $childOrderItem */
223
+ foreach ($ parentOrderItem ->getChildrenItems () as $ childOrderItem ) {
224
+ if (!isset ($ orderItemsQtyToInvoice [$ childOrderItem ->getItemId ()])) {
225
+ $ productOptions = $ childOrderItem ->getProductOptions ();
218
226
219
- if ($ orderItem ->isDummy ()) {
220
- if ($ orderItem ->getHasChildren ()) {
221
- foreach ($ orderItem ->getChildrenItems () as $ child ) {
222
- if (!isset ($ qtys [$ child ->getId ()])) {
223
- $ qtys [$ child ->getId ()] = $ child ->getQtyToInvoice ();
224
- }
225
- $ parentId = $ orderItem ->getParentItemId ();
226
- if ($ parentId && array_key_exists ($ parentId , $ qtys )) {
227
- $ qtys [$ orderItem ->getId ()] = $ qtys [$ parentId ];
228
- } else {
229
- continue ;
230
- }
231
- }
232
- } elseif ($ orderItem ->getParentItem ()) {
233
- $ parent = $ orderItem ->getParentItem ();
234
- if (!isset ($ qtys [$ parent ->getId ()])) {
235
- $ qtys [$ parent ->getId ()] = $ parent ->getQtyToInvoice ();
227
+ if (isset ($ productOptions ['bundle_selection_attributes ' ])) {
228
+ $ bundleSelectionAttributes = $ this ->serializer
229
+ ->unserialize ($ productOptions ['bundle_selection_attributes ' ]);
230
+ $ orderItemsQtyToInvoice [$ childOrderItem ->getItemId ()] =
231
+ $ bundleSelectionAttributes ['qty ' ] * $ orderItemsQtyToInvoice [$ parentOrderItem ->getItemId ()];
236
232
}
237
233
}
238
234
}
239
- }
240
-
241
- /**
242
- * Prepare qty to invoice for bundle products
243
- *
244
- * @param \Magento\Sales\Api\Data\OrderItemInterface $orderItem
245
- * @param array $qtys
246
- */
247
- private function prepareBundleQty (\Magento \Sales \Api \Data \OrderItemInterface $ orderItem , &$ qtys )
248
- {
249
- if ($ orderItem ->getProductType () == Type::TYPE_BUNDLE && !$ orderItem ->isShipSeparately ()) {
250
- foreach ($ orderItem ->getChildrenItems () as $ childItem ) {
251
- $ bundleSelectionAttributes = $ childItem ->getProductOptionByCode ('bundle_selection_attributes ' );
252
- if (is_string ($ bundleSelectionAttributes )) {
253
- $ bundleSelectionAttributes = $ this ->serializer ->unserialize ($ bundleSelectionAttributes );
254
- }
255
235
256
- $ qtys [$ childItem ->getId ()] = $ qtys [$ orderItem ->getId ()] * $ bundleSelectionAttributes ['qty ' ];
257
- }
258
- }
236
+ return $ orderItemsQtyToInvoice ;
259
237
}
260
238
261
239
/**
262
240
* Check if order item can be invoiced.
263
241
*
264
- * @param \Magento\Sales\Api\Data\ OrderItemInterface $item
242
+ * @param OrderItemInterface $item
265
243
* @param array $qtys
266
244
* @return bool
267
245
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
268
246
*/
269
- protected function _canInvoiceItem ( \ Magento \ Sales \ Api \ Data \ OrderItemInterface $ item , array $ qtys = [])
247
+ private function canInvoiceItem ( OrderItemInterface $ item , array $ qtys): bool
270
248
{
271
249
if ($ item ->getLockedDoInvoice ()) {
272
250
return false ;
@@ -299,14 +277,14 @@ protected function _canInvoiceItem(\Magento\Sales\Api\Data\OrderItemInterface $i
299
277
}
300
278
301
279
/**
302
- * Set quantity to invoice item
280
+ * Set quantity to invoice item.
303
281
*
304
- * @param \Magento\Sales\Api\Data\ InvoiceItemInterface $item
282
+ * @param InvoiceItemInterface $item
305
283
* @param float $qty
306
- * @return $this
307
- * @throws \Magento\Framework\Exception\ LocalizedException
284
+ * @return InvoiceManagementInterface
285
+ * @throws LocalizedException
308
286
*/
309
- protected function setInvoiceItemQuantity (\ Magento \ Sales \ Api \ Data \ InvoiceItemInterface $ item , $ qty )
287
+ private function setInvoiceItemQuantity (InvoiceItemInterface $ item , float $ qty ): InvoiceManagementInterface
310
288
{
311
289
$ qty = ($ item ->getOrderItem ()->getIsQtyDecimal ()) ? (double ) $ qty : (int ) $ qty ;
312
290
$ qty = $ qty > 0 ? $ qty : 0 ;
@@ -317,7 +295,7 @@ protected function setInvoiceItemQuantity(\Magento\Sales\Api\Data\InvoiceItemInt
317
295
$ qtyToInvoice = sprintf ("%F " , $ item ->getOrderItem ()->getQtyToInvoice ());
318
296
$ qty = sprintf ("%F " , $ qty );
319
297
if ($ qty > $ qtyToInvoice && !$ item ->getOrderItem ()->isDummy ()) {
320
- throw new \ Magento \ Framework \ Exception \ LocalizedException (
298
+ throw new LocalizedException (
321
299
__ ('We found an invalid quantity to invoice item "%1". ' , $ item ->getName ())
322
300
);
323
301
}
0 commit comments