OrderStatusWriter::writeItem()   C
last analyzed

Complexity

Conditions 9
Paths 37

Size

Total Lines 53
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 53
rs 6.8963
cc 9
eloc 25
nc 37
nop 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Jh\DataImportMagento\Writer;
4
5
use Ddeboer\DataImport\Exception\WriterException;
6
use Ddeboer\DataImport\Writer\WriterInterface;
7
8
/**
9
 * Class OrderStatusWriter
10
 * @author Aydin Hassan <[email protected]>
11
 */
12
class OrderStatusWriter implements WriterInterface
13
{
14
    /**
15
     * @var \Mage_Core_Model_Resource_Transaction
16
     */
17
    protected $transactionResourceModel;
18
19
    /**
20
     * @var \Mage_Sales_Model_Order
21
     */
22
    protected $orderModel;
23
24
    /**
25
     * @var array
26
     */
27
    protected $options = [
28
        'order_id_field'            => 'increment_id',
29
        'send_shipment_email'       => true,
30
        'send_credit_memo_email'    => true,
31
    ];
32
33
    /**
34
     * @param \Mage_Sales_Model_Order $orderModel
35
     * @param \Mage_Core_Model_Resource_Transaction $transactionResourceModel
36
     */
37
    public function __construct(
38
        \Mage_Sales_Model_Order $orderModel,
39
        \Mage_Core_Model_Resource_Transaction $transactionResourceModel
40
    ) {
41
        $this->orderModel                   = $orderModel;
42
        $this->transactionResourceModel     = $transactionResourceModel;
43
    }
44
45
    /**
46
     * @param array $item
47
     * @return $this
48
     * @throws MagentoSaveException
49
     * @throws \Ddeboer\DataImport\Exception\WriterException
50
     */
51
    public function writeItem(array $item)
52
    {
53
54
        if (!isset($item['orderId'])) {
55
            throw new WriterException('order_id must be set');
56
        }
57
58
        $order = $this->getOrder($item['orderId']);
59
60
        $quantities             = $this->validateItemsToBeShipped($order, $item['items']);
61
        $alreadyRefunded        = $this->getItemsShipped($order);
62
        //TODO: Make this configurable - Some Returns will not include the total qty's shipped
63
        //TODO: Just the exact qty to ship.
64
        $shipmentQuantities     = $this->getActualShipmentCount($alreadyRefunded, $quantities);
65
66
        if (count($shipmentQuantities)) {
67
            //ship it
68
            //TODO: Check if successful by catching exceptions
69
            try {
70
                $this->ship($order, $shipmentQuantities);
71
            } catch (\Exception $e) {
72
                //shipment failed - exit now? or carry on
73
            }
74
        }
75
76
        $quantities         = $this->validateItemsToBeRefunded($order, $item['items']);
77
        $alreadyReturned    = $this->getItemsRefunded($order);
78
        //TODO: Make this configurable - Some Returns will not include the total qty's returned
79
        //TODO: Just the exact qty to return.
80
        $returnQuantities    = $this->getActualRefundCount($alreadyReturned, $quantities);
81
82
        if (count($returnQuantities)) {
83
            //credit memo it
84
            try {
85
                $this->creditMemo($order, $returnQuantities);
86
            } catch (\Exception $e) {
87
                //credit memo failed - exit now? or carry on
88
            }
89
        }
90
91
        if (isset($item['status']) && null !== $item['status']) {
92
            //Update Status
93
            $order->setStatus(strtolower($item['status']));
94
        }
95
96
        try {
97
            $order->save();
98
        } catch (\Exception $e) {
99
            throw new MagentoSaveException($e->getMessage());
100
        }
101
102
        return $this;
103
    }
104
105
    /**
106
     * Create a Credit Memo with the Specified Quantities
107
     *
108
     * @param \Mage_Sales_Model_Order $order
109
     * @param array $quantities
110
     */
111
    public function ship(\Mage_Sales_Model_Order $order, array $quantities)
112
    {
113
114
        if (!$order->canShip()) {
115
            //throw
116
        }
117
118
        $shipment = $order->prepareShipment($quantities);
119
120
        if ($this->options['send_shipment_email']) {
121
            $shipment->setEmailSent(1);
122
            $shipment->getOrder()->setCustomerNoteNotify(1);
123
        }
124
125
        $shipment->register();
126
127
        $shipment->getOrder()->setIsInProcess(true);
128
129
        $transactionSave = clone $this->transactionResourceModel;
130
        $transactionSave
131
            ->addObject($shipment)
132
            ->addObject($shipment->getOrder())
133
            ->save();
134
135
        if ($this->options['send_shipment_email']) {
136
            $shipment->sendEmail(true);
137
        }
138
    }
139
140
    /**
141
     * Create a Credit Memo with the Specified Quantities
142
     *
143
     * @param \Mage_Sales_Model_Order $order
144
     * @param array $quantities
145
     */
146
    public function creditMemo(\Mage_Sales_Model_Order $order, array $quantities)
147
    {
148
149
        if (!$order->canCreditmemo()) {
150
            //throw
151
        }
152
153
        $service = $this->getServiceForOrder($order);
154
155
        /** @var \Mage_Sales_Model_Order_Creditmemo $creditMemo */
156
        $creditMemo = $service->prepareCreditmemo([
157
            'qtys'              => $quantities,
158
            //TODO: Make this configurable - have an option whether to refund shipping or not
159
            //TODO: if yes, then grab the amount from the input data
160
            'shipping_amount'   => 0,
161
        ]);
162
163
        if ($this->options['send_credit_memo_email']) {
164
            $creditMemo->setEmailSent(1);
165
            $creditMemo->getOrder()->setCustomerNoteNotify(1);
166
        }
167
168
        //don't actually perform refund.
169
        //TODO: Make this configurable ^
170
        $creditMemo->addData([
171
            'offline_requested' => true,
172
        ]);
173
174
        $creditMemo->register();
175
176
        $transactionSave = clone $this->transactionResourceModel;
177
        $transactionSave
178
            ->addObject($creditMemo)
179
            ->addObject($creditMemo->getOrder())
180
            ->save();
181
182
        if ($this->options['send_credit_memo_email']) {
183
            $creditMemo->sendEmail(true);
184
        }
185
    }
186
187
188
    /**
189
     * @param int $orderId
190
     * @throws WriterException
191
     * @return \Mage_Sales_Model_Order
192
     */
193
    public function getOrder($orderId)
194
    {
195
        $order = clone $this->orderModel;
196
        $order->load($orderId, $this->options['order_id_field']);
197
198
        if (!$order->getId()) {
199
            throw new WriterException(
200
                sprintf(
201
                    'Cannot find order with id: "%s", using: "%s" as id field',
202
                    $orderId,
203
                    $this->options['order_id_field']
204
                )
205
            );
206
        }
207
208
        return $order;
209
    }
210
211
    /**
212
     * If we have an item which has a qty of 7 to be refunded. What this actually means is we
213
     * have refunded a total amount of 7, but part of that qty could have been refunded at an earlier time.
214
     * So we need to get the total of that item already refunded and minus it from the qty to be refunded.
215
     *
216
     * Imagine we receive an refund with qty of 7 to refund. We have already refunded 4 so we want to refund the
217
     * other 3. SO: QtyToRefund - AlreadyRefunded === ActualQtyToRefund.
218
     *
219
     * @param array $alreadyShipped
220
     * @param array $toShip
221
     * @return array
222
     */
223
    public function getActualShipmentCount(array $alreadyShipped, array $toShip)
224
    {
225
        $actualShip = [];
226
        foreach ($toShip as $itemId => $qty) {
227
            if (isset($alreadyShipped[$itemId])) {
228
                $actualShip[$itemId] = $qty - $alreadyShipped[$itemId];
229
            } else {
230
                $actualShip[$itemId] = $qty;
231
            }
232
233
            if ($actualShip[$itemId] == 0) {
234
                unset($actualShip[$itemId]);
235
            }
236
        }
237
        return $actualShip;
238
    }
239
240
    /**
241
     * @param \Mage_Sales_Model_Order $order
242
     * @return array
243
     */
244
    public function getItemsShipped(\Mage_Sales_Model_Order $order)
245
    {
246
        $items = [];
247
248
        /** @var \Mage_Sales_Model_Order_Shipment $shipment */
249
        foreach ($order->getShipmentsCollection() as $shipment) {
250
            /** @var \Mage_Sales_Model_Order_Shipment_Item $item */
251
            foreach ($shipment->getAllItems() as $item) {
252
                if (!isset($items[$item->getData('order_item_id')])) {
253
                    $items[$item->getData('order_item_id')] = $item->getQty();
254
                } else {
255
                    $items[$item->getData('order_item_id')] += $item->getQty();
256
                }
257
            }
258
        }
259
260
        return $items;
261
    }
262
263
    /**
264
     * @param \Mage_Sales_Model_Order $order
265
     * @param array $items
266
     * @return array
267
     * @throws WriterException
268
     */
269
    public function validateItemsToBeShipped(\Mage_Sales_Model_Order $order, array $items)
270
    {
271
        $return = [];
272
        foreach ($items as $item) {
273
            $orderItem = $order->getItemsCollection()->getItemByColumnValue('sku', $item['sku']);
274
            if (null === $orderItem) {
275
                throw new WriterException(
276
                    sprintf('Item with SKU: "%s" does not exist in Order: "%s"', $item['sku'], $order->getIncrementId())
277
                );
278
            }
279
280
            $return[$orderItem->getId()] = $item['qtyShipped'];
281
        }
282
283
        return $return;
284
    }
285
286
    /**
287
     * If we have an item which has a qty of 7 to be refunded. What this actually means is we
288
     * have refunded a total amount of 7, but part of that qty could have been refunded at an earlier time.
289
     * So we need to get the total of that item already refunded and minus it from the qty to be refunded.
290
     *
291
     * Imagine we receive an refund with qty of 7 to refund. We have already refunded 4 so we want to refund the
292
     * other 3. SO: QtyToRefund - AlreadyRefunded === ActualQtyToRefund.
293
     *
294
     * @param array $alreadyRefunded
295
     * @param array $toRefund
296
     * @return array
297
     */
298
    public function getActualRefundCount(array $alreadyRefunded, array $toRefund)
299
    {
300
        $actualRefund = [];
301
        foreach ($toRefund as $itemId => $qty) {
302
            if (isset($alreadyRefunded[$itemId])) {
303
                $actualRefund[$itemId] = $qty - $alreadyRefunded[$itemId];
304
            } else {
305
                $actualRefund[$itemId] = $qty;
306
            }
307
308
            if ($actualRefund[$itemId] == 0) {
309
                unset($actualRefund[$itemId]);
310
            }
311
        }
312
        return $actualRefund;
313
    }
314
315
    /**
316
     * @param \Mage_Sales_Model_Order $order
317
     * @return array
318
     */
319
    public function getItemsRefunded(\Mage_Sales_Model_Order $order)
320
    {
321
        $items = [];
322
323
        /** @var \Mage_Sales_Model_Order_Creditmemo $creditMemo */
324
        foreach ($order->getCreditmemosCollection() as $creditMemo) {
325
            /** @var \Mage_Sales_Model_Order_Creditmemo_Item $item */
326
            foreach ($creditMemo->getAllItems() as $item) {
327
                if (!isset($items[$item->getData('order_item_id')])) {
328
                    $items[$item->getData('order_item_id')] = $item->getQty();
329
                } else {
330
                    $items[$item->getData('order_item_id')] += $item->getQty();
331
                }
332
            }
333
        }
334
335
        return $items;
336
    }
337
338
    /**
339
     * @param \Mage_Sales_Model_Order $order
340
     * @param array $items
341
     * @return array
342
     * @throws WriterException
343
     */
344
    public function validateItemsToBeRefunded(\Mage_Sales_Model_Order $order, array $items)
345
    {
346
        $return = [];
347
        foreach ($items as $item) {
348
            $orderItem = $order->getItemsCollection()->getItemByColumnValue('sku', $item['sku']);
349
            if (null === $orderItem) {
350
                throw new WriterException(
351
                    sprintf('Item with SKU: "%s" does not exist in Order: "%s"', $item['sku'], $order->getIncrementId())
352
                );
353
            }
354
355
            $return[$orderItem->getId()] = $item['qtyCancelled'];
356
        }
357
358
        return $return;
359
    }
360
361
    /**
362
     * Wrap up the writer after all items have been written
363
     *
364
     * @return WriterInterface
365
     */
366
    public function finish()
367
    {
368
    }
369
370
    /**
371
     * Prepare the writer before writing the items
372
     *
373
     * @return WriterInterface
374
     */
375
    public function prepare()
376
    {
377
    }
378
379
    /**
380
     * @param \Mage_Sales_Model_Order $order
381
     * @return \Mage_Sales_Model_Service_Order
382
     */
383
    public function getServiceForOrder(\Mage_Sales_Model_Order $order)
384
    {
385
        return \Mage::getModel('sales/service_order', $order);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression \Mage::getModel('sales/service_order', $order); of type object|false adds false to the return on line 385 which is incompatible with the return type documented by Jh\DataImportMagento\Wri...ter::getServiceForOrder of type Mage_Sales_Model_Service_Order. It seems like you forgot to handle an error condition.
Loading history...
386
    }
387
}
388