Completed
Branch 1.3 (ca63b5)
by Morven
03:44
created

OrderFactory::getItems()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 27
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 16
c 1
b 0
f 0
nc 6
nop 0
dl 0
loc 27
rs 9.7333
1
<?php
2
3
namespace SilverCommerce\OrdersAdmin\Factory;
4
5
use SilverStripe\ORM\DataObject;
6
use SilverStripe\ORM\ValidationException;
7
use SilverStripe\Core\Config\Configurable;
8
use SilverStripe\Core\Injector\Injectable;
9
use SilverCommerce\OrdersAdmin\Model\Invoice;
10
use SilverCommerce\OrdersAdmin\Model\Estimate;
11
12
class OrderFactory
13
{
14
    use Injectable, Configurable;
15
16
    /**
17
     * The class this factory uses to create an estimate
18
     *
19
     * @var string
20
     */
21
    private static $estimate_class = Estimate::class;
22
23
    /**
24
     * The class this factory uses to create an order
25
     *
26
     * @var string
27
     */
28
    private static $invoice_class = Invoice::class;
29
30
    /**
31
     * Parameter on estimates/invoices that is used as a "Reference number"
32
     *
33
     * @var string
34
     */
35
    private static $order_ref_param = "Ref";
36
37
    /**
38
     * Are we working with an invoice or an estimate?
39
     *
40
     * @var bool
41
     */
42
    protected $is_invoice;
43
44
    /**
45
     * An instance of an Invoice/Estimate
46
     *
47
     * @var \SilverCommerce\OrdersAdmin\Model\Estimate
48
     */
49
    protected $order;
50
51
    /**
52
     * The reference number for the invoice (if null, a new invoice is created)
53
     *
54
     * @var int
55
     */
56
    protected $ref;
57
58
    /**
59
     * The estimate/invoice ID (if null, a new estimate/invoice is created)
60
     *
61
     * @var int
62
     */
63
    protected $id;
64
65
    /**
66
     * Create a new instance of the factory and setup the estimate/invoice
67
     *
68
     * @param bool $invoice Is this an invoice? If not create estimate.
69
     * @param int  $id      Provide order id if we want existing estimate/invoice
70
     * @param int  $ref     Provide reference if we want existing estimate/invoice
71
     *
72
     * @return self
73
     */
74
    public function __construct($invoice = false, $id = null, $ref = null)
75
    {
76
        $this->setIsInvoice($invoice);
77
78
        if (isset($id)) {
79
            $this->setId($id);
80
        }
81
82
        if (isset($ref)) {
83
            $this->setRef($ref);
84
        }
85
86
        $this->findOrMake();
87
    }
88
89
    /**
90
     * Attempt to either find an existing order, or make a new one.
91
     * (based on submitted ID/Ref)
92
     *
93
     * @return self
94
     */
95
    public function findOrMake()
96
    {
97
        $ref_param = $this->config()->order_ref_param;
98
        $invoice = $this->getIsInvoice();
99
        $id = $this->getId();
100
        $ref = $this->getRef();
101
        $order = null;
102
103
        if ($invoice) {
104
            $class = $this->config()->invoice_class;
105
        } else {
106
            $class = $this->config()->estimate_class;
107
        }
108
109
        if (!empty($id)) {
110
            $order = DataObject::get($class)->byID($id);
111
        }
112
113
        if (!empty($ref)) {
114
            $order = DataObject::get($class)
115
                ->filter(
116
                    [
117
                        $ref_param => $ref,
118
                        'ClassName' => $class
119
                    ]
120
                )->first();
121
        }
122
123
        // If we have not found an order, create a new one
124
        if (empty($order)) {
125
            $order = $class::create();
126
        }
127
128
        $this->order = $order;
129
130
        return $this;
131
    }
132
133
    /**
134
     * Add a line item to the current order based on the provided product
135
     *
136
     * @param DataObject $product Instance of the product we want to add
137
     * @param int        $qty     Quanty of items to add
138
     * @param bool       $lock    Should this item be locked (cannot change quantity)
139
     * @param array      $custom  List of customisations to add
140
     *
141
     * @return self
142
     */
143
    public function addItem(
144
        DataObject $product,
145
        int $qty = 1,
146
        bool $lock = false,
147
        array $custom = []
148
    ) {
149
        $factory = LineItemFactory::create()
150
            ->setProduct($product)
151
            ->setQuantity($qty)
152
            ->setLock($lock)
153
            ->setCustomisations($custom)
154
            ->makeItem()
155
            ->write();
156
157
        // First check if this item exists
158
        $items = $this->getItems();
159
        $existing = null;
160
161
        if ($items->count() > 0) {
162
            $existing = $items->find("Key", $factory->getKey());
163
        }
164
165
        // If object already in the cart, update quantity and delete new item
166
        // else add as usual
167
        if (isset($existing)) {
168
            $this->updateItem($existing->Key, $qty);
169
            $factory->delete();
170
        } else {
171
            if (!$factory->checkStockLevel()) {
172
                throw new ValidationException(
173
                    _t(
174
                        __CLASS__ . ".NotEnoughStock",
175
                        "There are not enough '{title}' in stock",
176
                        ['title' => $factory->getItem()->Title]
177
                    )
178
                );
179
            } else {
180
                $this->getItems()->add($factory->getItem());
181
            }
182
        }
183
184
        return $this;
185
    }
186
187
    /**
188
     * Update the quantity of a line item from the current order based on the
189
     * provided key
190
     *
191
     * @param string $key       The key of the item to remove
192
     * @param int    $qty       The amount to increment the item by
193
     * @param bool   $increment Should the quantity increase or change?
194
     *
195
     * @return self
196
     */
197
    public function updateItem(string $key, int $qty, bool $increment = true)
198
    {
199
        $item = $this->getItems()->find("Key", $key);
200
201
        if (!empty($item)) {
202
            $factory = LineItemFactory::create()->setItem($item)->update();
203
            $new_qty = ($increment) ? $factory->getQuantity() + $qty : $qty;
204
205
            $factory
206
                ->setQuantity($new_qty)
207
                ->update();
208
209
            if (!$factory->checkStockLevel()) {
210
                throw new ValidationException(
211
                    _t(
212
                        __CLASS__ . ".NotEnoughStock",
213
                        "There are not enough '{title}' in stock",
214
                        ['title' => $factory->getItem()->Title]
215
                    )
216
                );
217
            }
218
219
            $factory->write();
220
        }
221
222
        return $this;
223
    }
224
225
    /**
226
     * Remove a line item from the current order based on the provided key
227
     *
228
     * @param string $key The key of the item to remove
229
     *
230
     * @return self
231
     */
232
    public function removeItem(string $key)
233
    {
234
        $item = $this->getItems()->find("Key", $key);
235
236
        if (!empty($item)) {
237
            $item->delete();
238
        }
239
240
        return $this;
241
    }
242
243
    /**
244
     * Write the currently selected order
245
     *
246
     * @return self
247
     */
248
    public function write()
249
    {
250
        if (!empty($this->order)) {
251
            $this->order->write();
252
        }
253
254
        return $this;
255
    }
256
257
    /**
258
     * Delete the current Estimate/Invoice from the DB
259
     *
260
     * @return self
261
     */
262
    public function delete()
263
    {
264
        $order = $this->order;
265
        
266
        if (isset($this->order)) {
267
            $order->delete();
268
        }
269
270
        return $this;
271
    }
272
273
    /**
274
     * Get an instance of an Invoice/Estimate
275
     *
276
     * @return  \SilverCommerce\OrdersAdmin\Model\Estimate
277
     */
278
    public function getOrder()
279
    {
280
        return $this->order;
281
    }
282
283
    /**
284
     * Get the current Invoice/Estimate items list
285
     *
286
     * @throws \SilverStripe\ORM\ValidationException
287
     *
288
     * @return \SilverStripe\ORM\SS_List
289
     */
290
    protected function getItems()
291
    {
292
        $order = $this->getOrder();
293
        $association = null;
294
        $associations = array_merge(
295
            $order->hasMany(),
296
            $order->manyMany()
297
        );
298
299
        // Find an applicable association
300
        foreach ($associations as $key => $value) {
301
            $class = $value::create();
302
            if (is_a($class, LineItemFactory::ITEM_CLASS)) {
303
                $association = $key;
304
                break;
305
            }
306
        }
307
308
        if (empty($association)) {
309
            throw new ValidationException(_t(
310
                __CLASS__ . ".NoItems",
311
                "The class '{class}' has no item association",
312
                ['class' => $order->ClassName]
313
            ));
314
        }
315
        
316
        return $order->{$association}();
317
    }
318
319
    /**
320
     * Get are we working with an invoice or an estimate?
321
     *
322
     * @return boolean
323
     */
324
    public function getIsInvoice()
325
    {
326
        return $this->is_invoice;
327
    }
328
329
    /**
330
     * Set are we working with an invoice or an estimate?
331
     *
332
     * @param bool $invoice Are we working with an invoice or an estimate?
333
     *
334
     * @return self
335
     */
336
    public function setIsInvoice(bool $invoice)
337
    {
338
        $this->is_invoice = $invoice;
339
        return $this;
340
    }
341
342
    /**
343
     * Get the reference number for the invoice (if null, a new invoice is created)
344
     *
345
     * @return int
346
     */
347
    public function getRef()
348
    {
349
        return $this->ref;
350
    }
351
352
    /**
353
     * Set the reference number for the invoice (if null, a new invoice is created)
354
     *
355
     * @param int $ref reference number
356
     *
357
     * @return self
358
     */
359
    public function setRef(int $ref)
360
    {
361
        $this->ref = $ref;
362
        return $this;
363
    }
364
365
    /**
366
     * Get the estimate/invoice ID (if null, a new estimate/invoice is created)
367
     *
368
     * @return int
369
     */
370
    public function getId()
371
    {
372
        return $this->id;
373
    }
374
375
    /**
376
     * Set the estimate/invoice ID (if null, a new estimate/invoice is created)
377
     *
378
     * @param int $id estimate/invoice ID
379
     *
380
     * @return self
381
     */
382
    public function setId(int $id)
383
    {
384
        $this->id = $id;
385
        return $this;
386
    }
387
}
388