Completed
Push — master ( 016efb...ce48d3 )
by Nicolaas
13:13
created

OrderProcessQueue::getCMSFields()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 44
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 32
nc 2
nop 0
dl 0
loc 44
rs 8.8571
c 0
b 0
f 0
1
<?php
2
/**
3
 * This class provides a bunch of Meta Objects
4
 * that do not interact with the object at hand, but rather with the datalist as a whole.
5
 *
6
 */
7
8
class OrderProcessQueue extends DataObject
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
9
{
10
    private static $db = array(
11
        'DeferTimeInSeconds' => 'Int',
12
        'InProcess' => 'Boolean',
13
        'ProcessAttempts' => 'Int'
14
    );
15
16
    private static $has_one = array(
17
        'Order' => 'Order',
18
        'OrderStep' => 'OrderStep'
19
    );
20
21
    private static $indexes = array(
22
        'DeferTimeInSeconds' => true,
23
        'ProcessAttempts' => true
24
    );
25
26
    private static $casting = array(
27
        'ToBeProcessedAt' => 'SS_Datetime',
28
        'HasBeenInQueueSince' => 'SS_Datetime'
29
    );
30
31
    private static $default_sort = [
32
        'ID' => 'DESC'
33
    ];
34
35
    /**
36
     * standard SS variable.
37
     *
38
     * @var array
39
     */
40
    private static $summary_fields = array(
41
        'Order.Title' => 'Order',
42
        'Order.Status.Title' => 'Current Step',
43
        'ProcessAttempts' => 'Attempts',
44
        'ToBeProcessedAt.Nice' => 'To be processed at',
45
        'ToBeProcessedAt.Ago' => 'That is ...',
46
        'HasBeenInQueueForSince.Nice' => 'Added to queue ...',
47
        'InProcess.Nice' => 'Currently Running'
48
    );
49
50
    /**
51
     * standard SS variable.
52
     *
53
     * @var array
54
     */
55
    private static $searchable_fields = array(
56
        'OrderID' => array(
57
            'field' => 'NumericField',
58
            'title' => 'Order Number',
59
        )
60
    );
61
62
63
    /**
64
     * Standard SS method.
65
     *
66
     * @param Member $member
67
     *
68
     * @return bool
69
     */
70
    public function canCreate($member = null)
71
    {
72
        return false;
73
    }
74
75
    /**
76
     * Standard SS method.
77
     *
78
     * @param Member $member
79
     *
80
     * @return bool
81
     */
82
    public function canView($member = null)
83
    {
84
        if (! $member) {
85
            $member = Member::currentUser();
86
        }
87
        $extended = $this->extendedCan(__FUNCTION__, $member);
88
        if ($extended !== null) {
89
            return $extended;
90
        }
91
        if (Permission::checkMember($member, Config::inst()->get('EcommerceRole', 'admin_permission_code'))) {
92
            return true;
93
        }
94
        //is the member is a shop assistant they can always view it
95
        if (EcommerceRole::current_member_is_shop_assistant($member)) {
96
            return true;
97
        }
98
99
        return parent::canView($member);
100
    }
101
102
    /**
103
     * Standard SS method.
104
     *
105
     * @param Member $member
106
     *
107
     * @return bool
108
     */
109
    public function canEdit($member = null)
110
    {
111
        return false;
112
    }
113
114
    /**
115
     * Standard SS method
116
     * Queues can be deleted if needed.
117
     *
118
     * @param Member $member
119
     *
120
     * @return bool
121
     */
122
    public function canDelete($member = null)
123
    {
124
        return parent::canDelete($member);
125
    }
126
127
    /**
128
     * standard SS variable.
129
     *
130
     * @var string
131
     */
132
    private static $singular_name = 'Order To Be Processed';
133
    public function i18n_singular_name()
134
    {
135
        return _t('OrderProcessQueue.SINGULAR_NAME', 'Order In Queue');
136
    }
137
138
    /**
139
     * standard SS variable.
140
     *
141
     * @var string
142
     */
143
    private static $plural_name = 'Orders to be Processed';
144
    public function i18n_plural_name()
145
    {
146
        return _t('OrderProcessQueue.PLURAL_NAME', 'Orders In Queue');
147
    }
148
149
150
    /**
151
     * META METHOD: Add an order to the job list if it does not exist already.
152
     *
153
     * @param Order $order
154
     * @param Int   $deferInSeconds
155
     */
156
    public function AddOrderToQueue($order, $deferTimeInSeconds)
157
    {
158
        if(!$order || ! $order->ID) {
159
            user_error('No real order provided.');
160
        }
161
        $filter = array(
162
            'OrderID' => $order->ID,
163
            'OrderStepID' => $order->StatusID
164
        );
165
        $existingEntry = DataObject::get_one(
166
            'OrderProcessQueue',
167
            $filter,
168
            $cacheDataObjectGetOne = false
169
        );
170
        $filter['DeferTimeInSeconds'] = $deferTimeInSeconds;
171
        if (! $existingEntry) {
172
            $existingEntry = OrderProcessQueue::create($filter);
173
        } else {
174
            foreach ($filter as $field => $value) {
175
                $existingEntry->$field = $value;
176
            }
177
        }
178
        $existingEntry->write();
179
180
        return $existingEntry;
181
    }
182
183
    /**
184
     * META METHOD
185
     * processes the order ...
186
     * returns TRUE if SUCCESSFUL and a message if unsuccessful ...
187
     *
188
     *
189
     * @param  Order $order optional
190
     * @return boolean | string
191
     */
192
    public function process($order = null)
193
    {
194
        //find variables
195
        if( ! $order) {
196
            $order = $this->Order();
197
            $myQueueObject = $this;
198
        } else {
199
            $myQueueObject = $this->getQueueObject($order);
200
        }
201
        //delete if order is gone ...
202
        if($order) {
203
            //if order has moved already ... delete
204
            if(
205
                $order->IsCancelled() ||
206
                $order->IsArchived()
207
            ) {
208
                $myQueueObject->delete();
209
                $message = 'Order is archived already and/or cancelled.';
210
            } elseif(
211
                $this->OrderStepID > 0
212
                && (int)$order->StatusID !== (int)$myQueueObject->OrderStepID
213
            ) {
214
                $message = 'Order has already moved on.';
215
                $myQueueObject->delete();
216
217
            } else {
218
                if($myQueueObject) {
219
                    if ($myQueueObject->isReadyToGo()) {
220
                        $oldOrderStatusID = $order->StatusID;
221
                        $myQueueObject->InProcess = true;
222
                        $myQueueObject->ProcessAttempts = $myQueueObject->ProcessAttempts + 1;
223
                        $myQueueObject->write();
224
                        $order->tryToFinaliseOrder(
225
                            $tryAgain = false,
226
                            $fromOrderQueue = true
227
                        );
228
                        $newOrderStatusID = $order->StatusID;
229
                        if($oldOrderStatusID != $newOrderStatusID) {
230
                            $myQueueObject->delete();
231
                            return true;
232
                        } else {
233
                            $message = 'Attempt to move order was not successful.';
234
                            $myQueueObject->InProcess = false;
235
                            $myQueueObject->write();
236
                        }
237
                    } else  {
238
                        $message = 'Minimum order queue time has not been passed.';
239
                    }
240
241
                } else {
242
                    $message = 'Could not find queue object.';
243
                }
244
            }
245
        } else {
246
            $message = 'Can not find order.';
247
            $myQueueObject->delete();
248
        }
249
250
        return $message;
251
    }
252
253
    /**
254
     * META METHOD: returns the queue object if it exists
255
     *
256
     * @param  Order $order
257
     *
258
     * @return null |   OrderProcessQueue
259
     */
260
    public function getQueueObject($order)
261
    {
262
        $filter = array('OrderID' => $order->ID);
263
264
        return DataObject::get_one('OrderProcessQueue', $filter);
265
    }
266
267
    /**
268
     * META METHOD: Once you are done, you can remove the item like this ...
269
     *
270
     * @param  Order $order
271
     */
272
    public function removeOrderFromQueue($order)
273
    {
274
        $queueEntries = OrderProcessQueue::get()->filter(array('OrderID' => $order->ID));
275
        foreach($queueEntries as $queueEntry) {
276
            $queueEntry->delete();
277
        }
278
    }
279
280
    /**
281
     * META METHOD: returns a list of orders to be processed
282
     * @param int $id force this Order to be processed
283
     * @param int $limit total number of orders that can be retrieved at any one time
284
     *
285
     * @return DataList (of orders)
286
     */
287
    public function OrdersToBeProcessed($id = 0, $limit = 9999)
288
    {
289
290
        //we sort the order randomly so that we get a nice mixture
291
        //not always the same ones holding up the process
292
        $sql = '
293
            SELECT "OrderID"
294
            FROM "OrderProcessQueue"
295
            WHERE
296
                "InProcess" = 0
297
                AND
298
                (UNIX_TIMESTAMP("Created") + "DeferTimeInSeconds") < UNIX_TIMESTAMP()
299
            ORDER BY '.$this->sortPhrase().'
300
            LIMIT '.$limit.';
301
        ';
302
        $rows = DB::query($sql);
303
        $orderIDs = array($id => $id);
304
        foreach ($rows as $row) {
305
            $orderIDs[$row['OrderID']] = $row['OrderID'];
306
        }
307
308
        return Order::get()
309
            ->filter(array('ID' => $orderIDs))
310
            ->sort($this->sortPhraseForOrderIDs($orderIDs));
311
    }
312
313
    /**
314
     * META METHOD: all orders with a queue object
315
     * @param int $id force this Order to be processed
316
     * @param int $limit total number of orders that can be retrieved at any one time
317
     *
318
     * @return DataList (of orders)
319
     */
320
    public function AllOrdersInQueue($limit = 9999)
321
    {
322
        $orderIDs = OrderProcessQueue::get()->column('OrderID');
323
324
        return Order::get()
325
            ->filter(array('ID' => $orderIDs))
326
            ->sort($this->sortPhraseForOrderIDs($orderIDs))
327
            ->limit($limit);
328
    }
329
330
    /**
331
     * META METHOD: returns a list of orders NOT YET to be processed
332
     * @param int $limit total number of orders that can be retrieved at any one time
333
     *
334
     * @return DataList (of orders)
335
     */
336
    public function OrdersInQueueThatAreNotReady($limit = 9999)
337
    {
338
339
        //we sort the order randomly so that we get a nice mixture
340
        //not always the same ones holding up the process
341
        $sql = '
342
            SELECT "OrderID"
343
            FROM "OrderProcessQueue"
344
            WHERE
345
                (UNIX_TIMESTAMP("Created") + "DeferTimeInSeconds") >= UNIX_TIMESTAMP()
346
            ORDER BY '.$this->sortPhrase().'
347
            LIMIT '.$limit.';
348
        ';
349
        $rows = DB::query($sql);
350
        $orderIDs = array(0 => 0);
351
        foreach ($rows as $row) {
352
            $orderIDs[$row['OrderID']] = $row['OrderID'];
353
        }
354
355
        return Order::get()
356
            ->filter(array('ID' => $orderIDs))
357
            ->sort($this->sortPhraseForOrderIDs($orderIDs));
358
    }
359
360
    /**
361
     * non-database method of working out if an Order is ready to go.
362
     *
363
     * @return bool
364
     */
365
    public function isReadyToGo()
366
    {
367
        return (strtotime($this->Created) + $this->DeferTimeInSeconds) < time();
368
    }
369
370
    /**
371
     *
372
     * casted variable
373
     * @return SS_DateTime
374
     */
375
    public function ToBeProcessedAt()
376
    {
377
        return $this->getToBeProcessedAt();
378
    }
379
380
    /**
381
     *
382
     * casted variable
383
     * @return SS_DateTime
384
     */
385
    public function getToBeProcessedAt()
386
    {
387
        return DBField::create_field('SS_Datetime', (strtotime($this->Created) + $this->DeferTimeInSeconds));
388
    }
389
390
391
    /**
392
     *
393
     * casted variable
394
     * @return SS_DateTime
395
     */
396
    public function HasBeenInQueueForSince()
397
    {
398
        return $this->getHasBeenInQueueForSince();
399
    }
400
401
    /**
402
     *
403
     * casted variable
404
     * @return SS_DateTime
405
     */
406
    public function getHasBeenInQueueForSince()
407
    {
408
        return DBField::create_field('SS_Datetime', (strtotime($this->Created)));
409
    }
410
411
412
    /**
413
     * CMS Fields
414
     * @return FieldList
415
     */
416
    public function getCMSFields()
417
    {
418
        $fields = parent::getCMSFields();
419
        if($this->exists()) {
420
            $fields->addFieldToTab(
421
                'Root.Main',
422
                ReadonlyField::create(
423
                    'HasBeenInQueueForSinceCompilations',
424
                    _t('OrderProcessQueue.SINCE', 'In the queue since'),
425
                    $this->getHasBeenInQueueForSince()->Nice() . ' - ' . $this->getHasBeenInQueueForSince()->Ago()
426
                ),
427
                'DeferTimeInSeconds'
428
            );
429
            $fields->addFieldToTab(
430
                'Root.Main',
431
                ReadonlyField::create(
432
                    'ToBeProcessedAtCompilations',
433
                    _t('OrderProcessQueue.TO_BE_PROCESSED', 'To Be Processed'),
434
                    $this->getToBeProcessedAt()->Nice() . ' - ' . $this->getToBeProcessedAt()->Ago()
435
                ),
436
                'InProcess'
437
            );
438
            $fields->addFieldToTab(
439
                'Root.Main',
440
                LiteralField::create(
441
                    'processQueueNow',
442
                    '<h2>
443
                        <a href="/dev/tasks/EcommerceTaskProcessOrderQueue/?id='.$this->OrderID.'" target="_blank">'.
444
                            _t('OrderProcessQueue.PROCESS', 'Process now').
445
                        '</a>
446
                    </h2>'
447
                )
448
            );
449
            $fields->replaceField(
450
                'OrderID',
451
                CMSEditLinkField::create(
452
                    'OrderID',
453
                    'Order',
454
                    $this->Order()
455
                )
456
            );
457
        }
458
        return $fields;
459
    }
460
461
    public function requireDefaultRecords()
462
    {
463
        parent::requireDefaultRecords();
464
        $errors = OrderProcessQueue::get()->filter(array('OrderID' => 0));
465
        foreach($errors as $error) {
466
            DB::alteration_message(' DELETING ROGUE OrderProcessQueue', 'deleted');
467
            $error->delete();
468
        } else {
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected T_ELSE
Loading history...
469
            DB::alteration_message(' There are no NO rogue Orders in the OrderProcessQueue');
470
        }
471
    }
472
473
    protected function sortPhrase()
474
    {
475
        return '
476
            "ProcessAttempts" ASC,
477
            (UNIX_TIMESTAMP("Created") + "DeferTimeInSeconds") ASC
478
        ';
479
    }
480
481
    /**
482
     * sort phrase for orders, based in order IDs...
483
     * @param  array $orderIds
484
     * @return string
485
     */
486
    protected function sortPhraseForOrderIDs($orderIDs)
487
    {
488
        return 'FIELD("Order"."ID", '.implode(",", $orderIDs).')';
489
    }
490
491
}
492