Completed
Push — 1.3 ( 1b8afc...3f453c )
by Morven
04:47 queued 10s
created

OrderFactory::setCustomer()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
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
        // First check if this item exists
159
        $items = $this->getItems();
160
        $existing = null;
161
162
        if ($items->count() > 0) {
163
            $existing = $items->find("Key", $factory->getKey());
164
        }
165
166
        // If object already in the cart, update quantity and delete new item
167
        // else add as usual
168
        if (isset($existing)) {
169
            $this->updateItem($existing->Key, $qty);
170
            $factory->delete();
171
        } else {
172
            if (!$factory->checkStockLevel()) {
173
                throw new ValidationException(
174
                    _t(
175
                        __CLASS__ . ".NotEnoughStock",
176
                        "There are not enough '{title}' in stock",
177
                        ['title' => $factory->getItem()->Title]
178
                    )
179
                );
180
            } else {
181
                $this->getItems()->add($factory->getItem());
182
            }
183
        }
184
185
        return $this;
186
    }
187
188
    /**
189
     * Update the quantity of a line item from the current order based on the
190
     * provided key
191
     *
192
     * @param string $key       The key of the item to remove
193
     * @param int    $qty       The amount to increment the item by
194
     * @param bool   $increment Should the quantity increase or change?
195
     *
196
     * @return self
197
     */
198
    public function updateItem(string $key, int $qty, bool $increment = true)
199
    {
200
        $item = $this->getItems()->find("Key", $key);
201
202
        if (!empty($item)) {
203
            $factory = LineItemFactory::create()->setItem($item)->update();
204
            $new_qty = ($increment) ? $factory->getQuantity() + $qty : $qty;
205
206
            $factory
207
                ->setQuantity($new_qty)
208
                ->update();
209
210
            if (!$factory->checkStockLevel()) {
211
                throw new ValidationException(
212
                    _t(
213
                        __CLASS__ . ".NotEnoughStock",
214
                        "There are not enough '{title}' in stock",
215
                        ['title' => $factory->getItem()->Title]
216
                    )
217
                );
218
            }
219
220
            $factory->write();
221
        }
222
223
        return $this;
224
    }
225
226
    /**
227
     * Remove a line item from the current order based on the provided key
228
     *
229
     * @param string $key The key of the item to remove
230
     *
231
     * @return self
232
     */
233
    public function removeItem(string $key)
234
    {
235
        $item = $this->getItems()->find("Key", $key);
236
237
        if (!empty($item)) {
238
            $item->delete();
239
        }
240
241
        return $this;
242
    }
243
244
    /**
245
     * Add the provided customer to the Invoice/Estimate
246
     *
247
     * @param \SilverCommerce\ContactAdmin\Model\Contact $contact
248
     *
249
     * @return self
250
     */
251
    public function setCustomer(Contact $contact)
252
    {
253
        $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...
254
    }
255
256
    /**
257
     * Write the currently selected order
258
     *
259
     * @return self
260
     */
261
    public function write()
262
    {
263
        if (!empty($this->order)) {
264
            $this->order->write();
265
        }
266
267
        return $this;
268
    }
269
270
    /**
271
     * Delete the current Estimate/Invoice from the DB
272
     *
273
     * @return self
274
     */
275
    public function delete()
276
    {
277
        $order = $this->order;
278
        
279
        if (isset($this->order)) {
280
            $order->delete();
281
        }
282
283
        return $this;
284
    }
285
286
    /**
287
     * Get an instance of an Invoice/Estimate
288
     *
289
     * @return  \SilverCommerce\OrdersAdmin\Model\Estimate
290
     */
291
    public function getOrder()
292
    {
293
        return $this->order;
294
    }
295
296
    /**
297
     * Get the current Invoice/Estimate items list
298
     *
299
     * @throws \SilverStripe\ORM\ValidationException
300
     *
301
     * @return \SilverStripe\ORM\SS_List
302
     */
303
    protected function getItems()
304
    {
305
        $order = $this->getOrder();
306
        $association = null;
307
        $associations = array_merge(
308
            $order->hasMany(),
309
            $order->manyMany()
310
        );
311
312
        // Find an applicable association
313
        foreach ($associations as $key => $value) {
314
            $class = $value::create();
315
            if (is_a($class, LineItemFactory::ITEM_CLASS)) {
316
                $association = $key;
317
                break;
318
            }
319
        }
320
321
        if (empty($association)) {
322
            throw new ValidationException(_t(
323
                __CLASS__ . ".NoItems",
324
                "The class '{class}' has no item association",
325
                ['class' => $order->ClassName]
326
            ));
327
        }
328
        
329
        return $order->{$association}();
330
    }
331
332
    /**
333
     * Get are we working with an invoice or an estimate?
334
     *
335
     * @return boolean
336
     */
337
    public function getIsInvoice()
338
    {
339
        return $this->is_invoice;
340
    }
341
342
    /**
343
     * Set are we working with an invoice or an estimate?
344
     *
345
     * @param bool $invoice Are we working with an invoice or an estimate?
346
     *
347
     * @return self
348
     */
349
    public function setIsInvoice(bool $invoice)
350
    {
351
        $this->is_invoice = $invoice;
352
        return $this;
353
    }
354
355
    /**
356
     * Get the reference number for the invoice (if null, a new invoice is created)
357
     *
358
     * @return int
359
     */
360
    public function getRef()
361
    {
362
        return $this->ref;
363
    }
364
365
    /**
366
     * Set the reference number for the invoice (if null, a new invoice is created)
367
     *
368
     * @param int $ref reference number
369
     *
370
     * @return self
371
     */
372
    public function setRef(int $ref)
373
    {
374
        $this->ref = $ref;
375
        return $this;
376
    }
377
378
    /**
379
     * Get the estimate/invoice ID (if null, a new estimate/invoice is created)
380
     *
381
     * @return int
382
     */
383
    public function getId()
384
    {
385
        return $this->id;
386
    }
387
388
    /**
389
     * Set the estimate/invoice ID (if null, a new estimate/invoice is created)
390
     *
391
     * @param int $id estimate/invoice ID
392
     *
393
     * @return self
394
     */
395
    public function setId(int $id)
396
    {
397
        $this->id = $id;
398
        return $this;
399
    }
400
}
401