Passed
Push — 1.3 ( b80b37...7e2089 )
by Morven
03:53
created

OrderFactory::addFromLineItemFactory()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 30
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

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