Completed
Push — master ( f71c82...1db67b )
by
unknown
03:31
created

Order::canView()   B

Complexity

Conditions 11
Paths 9

Size

Total Lines 45

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
dl 0
loc 45
rs 7.3166
c 0
b 0
f 0
nc 9
nop 1

How to fix   Complexity   

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
/**
4
 * @description:
5
 * The order class is a databound object for handling Orders within SilverStripe.
6
 * Note that it works closely with the ShoppingCart class, which accompanies the Order
7
 * until it has been paid for / confirmed by the user.
8
 *
9
 *
10
 * CONTENTS:
11
 * ----------------------------------------------
12
 * 1. CMS STUFF
13
 * 2. MAIN TRANSITION FUNCTIONS
14
 * 3. STATUS RELATED FUNCTIONS / SHORTCUTS
15
 * 4. LINKING ORDER WITH MEMBER AND ADDRESS
16
 * 5. CUSTOMER COMMUNICATION
17
 * 6. ITEM MANAGEMENT
18
 * 7. CRUD METHODS (e.g. canView, canEdit, canDelete, etc...)
19
 * 8. GET METHODS (e.g. Total, SubTotal, Title, etc...)
20
 * 9. TEMPLATE RELATED STUFF
21
 * 10. STANDARD SS METHODS (requireDefaultRecords, onBeforeDelete, etc...)
22
 * 11. DEBUG
23
 *
24
 * @authors: Nicolaas [at] Sunny Side Up .co.nz
25
 * @package: ecommerce
26
 * @sub-package: model
27
 * @inspiration: Silverstripe Ltd, Jeremy
28
 *
29
 * NOTE: This is the SQL for selecting orders in sequence of
30
 **/
31
class Order extends DataObject implements EditableEcommerceObject
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...
32
{
33
    /**
34
     * API Control.
35
     *
36
     * @var array
37
     */
38
    private static $api_access = array(
0 ignored issues
show
Unused Code introduced by
The property $api_access is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
39
        'view' => array(
40
            'OrderEmail',
41
            'EmailLink',
42
            'PrintLink',
43
            'RetrieveLink',
44
            'ShareLink',
45
            'FeedbackLink',
46
            'Title',
47
            'Total',
48
            'SubTotal',
49
            'TotalPaid',
50
            'TotalOutstanding',
51
            'ExchangeRate',
52
            'CurrencyUsed',
53
            'TotalItems',
54
            'TotalItemsTimesQuantity',
55
            'IsCancelled',
56
            'Country',
57
            'FullNameCountry',
58
            'IsSubmitted',
59
            'CustomerStatus',
60
            'CanHaveShippingAddress',
61
            'CancelledBy',
62
            'CurrencyUsed',
63
            'BillingAddress',
64
            'UseShippingAddress',
65
            'ShippingAddress',
66
            'Status',
67
            'Attributes',
68
            'OrderStatusLogs',
69
            'MemberID',
70
        ),
71
    );
72
73
    /**
74
     * standard SS variable.
75
     *
76
     * @var array
77
     */
78
    private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $db is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
79
        'SessionID' => 'Varchar(32)', //so that in the future we can link sessions with Orders.... One session can have several orders, but an order can onnly have one session
80
        'UseShippingAddress' => 'Boolean',
81
        'CustomerOrderNote' => 'Text',
82
        'ExchangeRate' => 'Double',
83
        //'TotalItems_Saved' => 'Double',
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
84
        //'TotalItemsTimesQuantity_Saved' => 'Double'
85
    );
86
87
    private static $has_one = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $has_one is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
88
        'Member' => 'Member',
89
        'BillingAddress' => 'BillingAddress',
90
        'ShippingAddress' => 'ShippingAddress',
91
        'Status' => 'OrderStep',
92
        'CancelledBy' => 'Member',
93
        'CurrencyUsed' => 'EcommerceCurrency',
94
    );
95
96
    /**
97
     * standard SS variable.
98
     *
99
     * @var array
100
     */
101
    private static $has_many = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $has_many is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
102
        'Attributes' => 'OrderAttribute',
103
        'OrderStatusLogs' => 'OrderStatusLog',
104
        'Payments' => 'EcommercePayment',
105
        'Emails' => 'OrderEmailRecord',
106
        'OrderProcessQueue' => 'OrderProcessQueue' //there is usually only one.
107
    );
108
109
    /**
110
     * standard SS variable.
111
     *
112
     * @var array
113
     */
114
    private static $indexes = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $indexes is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
115
        'SessionID' => true,
116
        'LastEdited' => true
117
    );
118
119
    /**
120
     * standard SS variable.
121
     *
122
     * @var string
123
     */
124
    private static $default_sort = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $default_sort is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
125
        'LastEdited' => 'DESC',
126
        'ID' => 'DESC'
127
    ];
128
129
    /**
130
     * standard SS variable.
131
     *
132
     * @var array
133
     */
134
    private static $casting = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $casting is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
135
        'OrderEmail' => 'Varchar',
136
        'EmailLink' => 'Varchar',
137
        'PrintLink' => 'Varchar',
138
        'ShareLink' => 'Varchar',
139
        'FeedbackLink' => 'Varchar',
140
        'RetrieveLink' => 'Varchar',
141
        'Title' => 'Varchar',
142
        'Total' => 'Currency',
143
        'TotalAsMoney' => 'Money',
144
        'SubTotal' => 'Currency',
145
        'SubTotalAsMoney' => 'Money',
146
        'TotalPaid' => 'Currency',
147
        'TotalPaidAsMoney' => 'Money',
148
        'TotalOutstanding' => 'Currency',
149
        'TotalOutstandingAsMoney' => 'Money',
150
        'HasAlternativeCurrency' => 'Boolean',
151
        'TotalItems' => 'Double',
152
        'TotalItemsTimesQuantity' => 'Double',
153
        'IsCancelled' => 'Boolean',
154
        'IsPaidNice' => 'Varchar',
155
        'Country' => 'Varchar(3)', //This is the applicable country for the order - for tax purposes, etc....
156
        'FullNameCountry' => 'Varchar',
157
        'IsSubmitted' => 'Boolean',
158
        'CustomerStatus' => 'Varchar',
159
        'CanHaveShippingAddress' => 'Boolean',
160
    );
161
162
    /**
163
     * standard SS variable.
164
     *
165
     * @var string
166
     */
167
    private static $singular_name = 'Order';
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $singular_name is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
168
    public function i18n_singular_name()
169
    {
170
        return _t('Order.ORDER', 'Order');
171
    }
172
173
    /**
174
     * standard SS variable.
175
     *
176
     * @var string
177
     */
178
    private static $plural_name = 'Orders';
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $plural_name is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
179
    public function i18n_plural_name()
180
    {
181
        return _t('Order.ORDERS', 'Orders');
182
    }
183
184
    /**
185
     * Standard SS variable.
186
     *
187
     * @var string
188
     */
189
    private static $description = "A collection of items that together make up the 'Order'.  An order can be placed.";
0 ignored issues
show
Unused Code introduced by
The property $description is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
190
191
    /**
192
     * Tells us if an order needs to be recalculated
193
     * can save one for each order...
194
     *
195
     * @var array
196
     */
197
    private static $_needs_recalculating = array();
198
199
    /**
200
     * @param bool (optional) $b
201
     * @param int (optional)  $orderID
202
     *
203
     * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
204
     */
205
    public static function set_needs_recalculating($b = true, $orderID = 0)
206
    {
207
        self::$_needs_recalculating[$orderID] = $b;
208
    }
209
210
    /**
211
     * @param int (optional) $orderID
212
     *
213
     * @return bool
214
     */
215
    public static function get_needs_recalculating($orderID = 0)
216
    {
217
        return isset(self::$_needs_recalculating[$orderID]) ? self::$_needs_recalculating[$orderID] : false;
218
    }
219
220
    /**
221
     * Total Items : total items in cart
222
     * We start with -1 to easily identify if it has been run before.
223
     *
224
     * @var int
225
     */
226
    protected $totalItems = null;
227
228
    /**
229
     * Total Items : total items in cart
230
     * We start with -1 to easily identify if it has been run before.
231
     *
232
     * @var float
233
     */
234
    protected $totalItemsTimesQuantity = null;
235
236
    /**
237
     * Returns a set of modifier forms for use in the checkout order form,
238
     * Controller is optional, because the orderForm has its own default controller.
239
     *
240
     * This method only returns the Forms that should be included outside
241
     * the editable table... Forms within it can be called
242
     * from through the modifier itself.
243
     *
244
     * @param Controller $optionalController
0 ignored issues
show
Documentation introduced by
Should the type for parameter $optionalController not be null|Controller?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
245
     * @param Validator  $optionalValidator
0 ignored issues
show
Documentation introduced by
Should the type for parameter $optionalValidator not be null|Validator?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
246
     *
247
     * @return ArrayList (ModifierForms) | Null
0 ignored issues
show
Documentation introduced by
Should the return type not be ArrayList|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
248
     **/
249
    public function getModifierForms(Controller $optionalController = null, Validator $optionalValidator = null)
250
    {
251
        $arrayList = new ArrayList();
252
        $modifiers = $this->Modifiers();
253
        if ($modifiers->count()) {
254
            foreach ($modifiers as $modifier) {
255
                if ($modifier->ShowForm()) {
256
                    if ($form = $modifier->getModifierForm($optionalController, $optionalValidator)) {
257
                        $form->ShowFormInEditableOrderTable = $modifier->ShowFormInEditableOrderTable();
258
                        $form->ShowFormOutsideEditableOrderTable = $modifier->ShowFormOutsideEditableOrderTable();
259
                        $form->ModifierName = $modifier->ClassName;
260
                        $arrayList->push($form);
261
                    }
262
                }
263
            }
264
        }
265
        if ($arrayList->count()) {
266
            return $arrayList;
267
        } else {
268
            return;
269
        }
270
    }
271
272
    /**
273
     * This function returns the OrderSteps.
274
     *
275
     * @return ArrayList (OrderSteps)
0 ignored issues
show
Documentation introduced by
Should the return type not be DataList?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
276
     **/
277
    public static function get_order_status_options()
278
    {
279
        return OrderStep::get();
280
    }
281
282
    /**
283
     * Like the standard byID, but it checks whether we are allowed to view the order.
284
     *
285
     * @return: Order | Null
286
     **/
287
    public static function get_by_id_if_can_view($id)
288
    {
289
        $order = Order::get()->byID($id);
290
        if ($order && $order->canView()) {
291
            if ($order->IsSubmitted()) {
292
                // LITTLE HACK TO MAKE SURE WE SHOW THE LATEST INFORMATION!
293
                $order->tryToFinaliseOrder();
294
            }
295
296
            return $order;
297
        }
298
299
        return;
300
    }
301
302
    /**
303
     * returns a Datalist with the submitted order log included
304
     * this allows you to sort the orders by their submit dates.
305
     * You can retrieve this list and then add more to it (e.g. additional filters, additional joins, etc...).
306
     *
307
     * @param bool $onlySubmittedOrders - only include Orders that have already been submitted.
308
     * @param bool $includeCancelledOrders - only include Orders that have already been submitted.
309
     *
310
     * @return DataList (Orders)
311
     */
312
    public static function get_datalist_of_orders_with_submit_record($onlySubmittedOrders = true, $includeCancelledOrders = false)
313
    {
314
        if ($onlySubmittedOrders) {
315
            $submittedOrderStatusLogClassName = EcommerceConfig::get('OrderStatusLog', 'order_status_log_class_used_for_submitting_order');
316
            $list = Order::get()
317
                ->LeftJoin('OrderStatusLog', '"Order"."ID" = "OrderStatusLog"."OrderID"')
318
                ->LeftJoin($submittedOrderStatusLogClassName, '"OrderStatusLog"."ID" = "'.$submittedOrderStatusLogClassName.'"."ID"')
319
                ->Sort('OrderStatusLog.Created', 'ASC');
320
            $where = ' ("OrderStatusLog"."ClassName" = \''.$submittedOrderStatusLogClassName.'\') ';
321
        } else {
322
            $list = Order::get();
323
            $where = ' ("StatusID" > 0) ';
324
        }
325
        if ($includeCancelledOrders) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
326
            //do nothing...
327
        } else {
328
            $where .= ' AND ("CancelledByID" = 0 OR "CancelledByID" IS NULL)';
329
        }
330
        $list = $list->where($where);
331
332
        return $list;
333
    }
334
335
    /*******************************************************
336
       * 1. CMS STUFF
337
    *******************************************************/
338
339
    /**
340
     * fields that we remove from the parent::getCMSFields object set.
341
     *
342
     * @var array
343
     */
344
    protected $fieldsAndTabsToBeRemoved = array(
345
        'MemberID',
346
        'Attributes',
347
        'SessionID',
348
        'Emails',
349
        'BillingAddressID',
350
        'ShippingAddressID',
351
        'UseShippingAddress',
352
        'OrderStatusLogs',
353
        'Payments',
354
        'OrderDate',
355
        'ExchangeRate',
356
        'CurrencyUsedID',
357
        'StatusID',
358
        'Currency',
359
    );
360
361
    /**
362
     * STANDARD SILVERSTRIPE STUFF.
363
     **/
364
    private static $summary_fields = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $summary_fields is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
365
        'Title' => 'Title',
366
        'OrderItemsSummaryNice' => 'Order Items',
367
        'Status.Title' => 'Next Step',
368
        'Member.Surname' => 'Last Name',
369
        'Member.Email' => 'Email',
370
        'TotalAsMoney.Nice' => 'Total',
371
        'TotalItemsTimesQuantity' => 'Units',
372
        'IsPaidNice' => 'Paid'
373
    );
374
375
    /**
376
     * STANDARD SILVERSTRIPE STUFF.
377
     *
378
     * @todo: how to translate this?
379
     **/
380
    private static $searchable_fields = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $searchable_fields is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
381
        'ID' => array(
382
            'field' => 'NumericField',
383
            'title' => 'Order Number',
384
        ),
385
        'MemberID' => array(
386
            'field' => 'TextField',
387
            'filter' => 'OrderFilters_MemberAndAddress',
388
            'title' => 'Customer Details',
389
        ),
390
        'Created' => array(
391
            'field' => 'TextField',
392
            'filter' => 'OrderFilters_AroundDateFilter',
393
            'title' => 'Date (e.g. Today, 1 jan 2007, or last week)',
394
        ),
395
        //make sure to keep the items below, otherwise they do not show in form
396
        'StatusID' => array(
397
            'filter' => 'OrderFilters_MultiOptionsetStatusIDFilter',
398
        ),
399
        'CancelledByID' => array(
400
            'filter' => 'OrderFilters_HasBeenCancelled',
401
            'title' => 'Cancelled by ...',
402
        ),
403
    );
404
405
    /**
406
     * Determine which properties on the DataObject are
407
     * searchable, and map them to their default {@link FormField}
408
     * representations. Used for scaffolding a searchform for {@link ModelAdmin}.
409
     *
410
     * Some additional logic is included for switching field labels, based on
411
     * how generic or specific the field type is.
412
     *
413
     * Used by {@link SearchContext}.
414
     *
415
     * @param array $_params
0 ignored issues
show
Documentation introduced by
Should the type for parameter $_params not be array|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
416
     *                       'fieldClasses': Associative array of field names as keys and FormField classes as values
417
     *                       'restrictFields': Numeric array of a field name whitelist
418
     *
419
     * @return FieldList
420
     */
421
    public function scaffoldSearchFields($_params = null)
0 ignored issues
show
Coding Style introduced by
scaffoldSearchFields uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
422
    {
423
        $fieldList = parent::scaffoldSearchFields($_params);
424
425
        //for sales to action only show relevant ones ...
426
        if (Controller::curr() && Controller::curr()->class === 'SalesAdmin') {
427
            $statusOptions = OrderStep::admin_manageable_steps();
428
        } else {
429
            $statusOptions = OrderStep::get();
430
        }
431
        if ($statusOptions && $statusOptions->count()) {
432
            $createdOrderStatusID = 0;
0 ignored issues
show
Unused Code introduced by
$createdOrderStatusID is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
433
            $preSelected = array();
434
            $createdOrderStatus = $statusOptions->First();
435
            if ($createdOrderStatus) {
436
                $createdOrderStatusID = $createdOrderStatus->ID;
0 ignored issues
show
Unused Code introduced by
$createdOrderStatusID is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
437
            }
438
            $arrayOfStatusOptions = clone $statusOptions->map('ID', 'Title');
439
            $arrayOfStatusOptionsFinal = array();
440
            if (count($arrayOfStatusOptions)) {
441
                foreach ($arrayOfStatusOptions as $key => $value) {
442
                    if (isset($_GET['q']['StatusID'][$key])) {
443
                        $preSelected[$key] = $key;
444
                    }
445
                    $count = Order::get()
446
                        ->Filter(array('StatusID' => intval($key)))
447
                        ->count();
448
                    if ($count < 1) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
449
                        //do nothing
450
                    } else {
451
                        $arrayOfStatusOptionsFinal[$key] = $value." ($count)";
452
                    }
453
                }
454
            }
455
            $statusField = new CheckboxSetField(
456
                'StatusID',
457
                Injector::inst()->get('OrderStep')->i18n_singular_name(),
458
                $arrayOfStatusOptionsFinal,
459
                $preSelected
0 ignored issues
show
Documentation introduced by
$preSelected is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
460
            );
461
            $fieldList->push($statusField);
462
        }
463
        $fieldList->push(new DropdownField('CancelledByID', 'Cancelled', array(-1 => '(Any)', 1 => 'yes', 0 => 'no')));
464
465
        //allow changes
466
        $this->extend('scaffoldSearchFields', $fieldList, $_params);
467
468
        return $fieldList;
469
    }
470
471
    /**
472
     * link to edit the record.
473
     *
474
     * @param string | Null $action - e.g. edit
475
     *
476
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be null|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
477
     */
478
    public function CMSEditLink($action = null)
479
    {
480
        return CMSEditLinkAPI::find_edit_link_for_object($this, $action, 'sales-advanced');
481
    }
482
483
    /**
484
     * STANDARD SILVERSTRIPE STUFF
485
     * broken up into submitted and not (yet) submitted.
486
     **/
487
    public function getCMSFields()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
Coding Style introduced by
getCMSFields uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
488
    {
489
        $fields = $this->scaffoldFormFields(array(
490
            // Don't allow has_many/many_many relationship editing before the record is first saved
491
            'includeRelations' => false,
492
            'tabbed' => true,
493
            'ajaxSafe' => true
494
        ));
495
        $fields->insertBefore(
496
            Tab::create(
497
                'Next',
498
                _t('Order.NEXT_TAB', 'Action')
499
            ),
500
            'Main'
0 ignored issues
show
Documentation introduced by
'Main' is of type string, but the function expects a object<FormField>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
501
        );
502
        $fields->addFieldsToTab(
503
            'Root',
504
            array(
505
                Tab::create(
506
                    "Items",
507
                    _t('Order.ITEMS_TAB', 'Items')
508
                ),
509
                Tab::create(
510
                    "Extras",
511
                    _t('Order.MODIFIERS_TAB', 'Adjustments')
512
                ),
513
                Tab::create(
514
                    'Emails',
515
                    _t('Order.EMAILS_TAB', 'Emails')
516
                ),
517
                Tab::create(
518
                    'Payments',
519
                    _t('Order.PAYMENTS_TAB', 'Payment')
520
                ),
521
                Tab::create(
522
                    'Account',
523
                    _t('Order.ACCOUNT_TAB', 'Account')
524
                ),
525
                Tab::create(
526
                    'Currency',
527
                    _t('Order.CURRENCY_TAB', 'Currency')
528
                ),
529
                Tab::create(
530
                    'Addresses',
531
                    _t('Order.ADDRESSES_TAB', 'Addresses')
532
                ),
533
                Tab::create(
534
                    'Log',
535
                    _t('Order.LOG_TAB', 'Notes')
536
                ),
537
                Tab::create(
538
                    'Cancellations',
539
                    _t('Order.CANCELLATION_TAB', 'Cancel')
540
                ),
541
            )
542
        );
543
        //as we are no longer using the parent:;getCMSFields
544
        // we had to add the updateCMSFields hook.
545
        $this->extend('updateCMSFields', $fields);
546
        $currentMember = Member::currentUser();
547
        if (!$this->exists() || !$this->StatusID) {
0 ignored issues
show
Documentation introduced by
The property StatusID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
548
            $firstStep = DataObject::get_one('OrderStep');
549
            $this->StatusID = $firstStep->ID;
0 ignored issues
show
Documentation introduced by
The property StatusID does not exist on object<Order>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
550
            $this->write();
551
        }
552
        $submitted = $this->IsSubmitted() ? true : false;
553
        if ($submitted) {
554
            //TODO
555
            //Having trouble here, as when you submit the form (for example, a payment confirmation)
556
            //as the step moves forward, meaning the fields generated are incorrect, causing an error
557
            //"I can't handle sub-URLs of a Form object." generated by the RequestHandler.
558
            //Therefore we need to try reload the page so that it will be requesting the correct URL to generate the correct fields for the current step
559
            //Or something similar.
560
            //why not check if the URL == $this->CMSEditLink()
561
            //and only tryToFinaliseOrder if this is true....
562
            if ($_SERVER['REQUEST_URI'] == $this->CMSEditLink() || $_SERVER['REQUEST_URI'] == $this->CMSEditLink('edit')) {
563
                $this->tryToFinaliseOrder();
564
            }
565
        } else {
566
            $this->init(true);
567
            $this->calculateOrderAttributes(true);
568
            Session::set('EcommerceOrderGETCMSHack', $this->ID);
569
        }
570
        if ($submitted) {
571
            $this->fieldsAndTabsToBeRemoved[] = 'CustomerOrderNote';
572
        } else {
573
            $this->fieldsAndTabsToBeRemoved[] = 'Emails';
574
        }
575
        foreach ($this->fieldsAndTabsToBeRemoved as $field) {
576
            $fields->removeByName($field);
577
        }
578
        $orderSummaryConfig = GridFieldConfig_Base::create();
579
        $orderSummaryConfig->removeComponentsByType('GridFieldToolbarHeader');
580
        // $orderSummaryConfig->removeComponentsByType('GridFieldSortableHeader');
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
581
        $orderSummaryConfig->removeComponentsByType('GridFieldFilterHeader');
582
        $orderSummaryConfig->removeComponentsByType('GridFieldPageCount');
583
        $orderSummaryConfig->removeComponentsByType('GridFieldPaginator');
584
        $nextFieldArray = array(
585
            LiteralField::create('CssFix', '<style>#Root_Next h2.form-control {padding: 0!important; margin: 0!important; padding-top: 4em!important;}</style>'),
586
            HeaderField::create('MyOrderStepHeader', _t('Order.CURRENT_STATUS', '1. Current Status')),
587
            $this->OrderStepField(),
588
            GridField::create(
589
                'OrderSummary',
590
                _t('Order.CURRENT_STATUS', 'Summary'),
591
                ArrayList::create(array($this)),
592
                $orderSummaryConfig
593
            )
594
        );
595
        $keyNotes = OrderStatusLog::get()->filter(
596
            array(
597
                'OrderID' => $this->ID,
598
                'ClassName' => 'OrderStatusLog'
599
            )
600
        );
601
        if ($keyNotes->count()) {
602
            $notesSummaryConfig = GridFieldConfig_RecordViewer::create();
603
            $notesSummaryConfig->removeComponentsByType('GridFieldToolbarHeader');
604
            $notesSummaryConfig->removeComponentsByType('GridFieldFilterHeader');
605
            // $orderSummaryConfig->removeComponentsByType('GridFieldSortableHeader');
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
606
            $notesSummaryConfig->removeComponentsByType('GridFieldPageCount');
607
            $notesSummaryConfig->removeComponentsByType('GridFieldPaginator');
608
            $nextFieldArray = array_merge(
609
                $nextFieldArray,
610
                array(
611
                    HeaderField::create('KeyNotesHeader', _t('Order.KEY_NOTES_HEADER', 'Key Notes')),
612
                    GridField::create(
613
                        'OrderStatusLogSummary',
614
                        _t('Order.CURRENT_KEY_NOTES', 'Key Notes'),
615
                        $keyNotes,
616
                        $notesSummaryConfig
617
                    )
618
                )
619
            );
620
        }
621
        $nextFieldArray = array_merge(
622
            $nextFieldArray,
623
            array(
624
                EcommerceCMSButtonField::create(
625
                    'AddNoteButton',
626
                    '/admin/sales/Order/EditForm/field/Order/item/' . $this->ID . '/ItemEditForm/field/OrderStatusLog/item/new',
627
                    _t('Order.ADD_NOTE', 'Add Note')
628
                )
629
            )
630
        );
631
        $nextFieldArray = array_merge(
632
            $nextFieldArray,
633
            array(
634
635
            )
636
        );
637
638
        //is the member is a shop admin they can always view it
639
640
        if (EcommerceRole::current_member_can_process_orders(Member::currentUser())) {
641
            $lastStep = OrderStep::last_order_step();
642
            if ($this->StatusID != $lastStep->ID) {
0 ignored issues
show
Documentation introduced by
The property StatusID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
643
                $queueObjectSingleton = Injector::inst()->get('OrderProcessQueue');
644
                if ($myQueueObject = $queueObjectSingleton->getQueueObject($this)) {
0 ignored issues
show
Unused Code introduced by
$myQueueObject is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
645
                    $myQueueObjectField = GridField::create(
646
                        'MyQueueObjectField',
647
                        _t('Order.QUEUE_DETAILS', 'Queue Details'),
648
                        $this->OrderProcessQueue(),
0 ignored issues
show
Documentation Bug introduced by
The method OrderProcessQueue does not exist on object<Order>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
649
                        GridFieldConfig_RecordEditor::create()
650
                    );
651
                } else {
652
                    $myQueueObjectField = LiteralField::create('MyQueueObjectField', '<p>'._t('Order.NOT_QUEUED', 'This order is not queued for future processing.').'</p>');
653
                }
654
                $nextFieldArray = array_merge(
655
                    $nextFieldArray,
656
                    array(
657
                        HeaderField::create('OrderStepNextStepHeader', _t('Order.ACTION_NEXT_STEP', '2. Action Next Step')),
658
                        $myQueueObjectField,
659
                        HeaderField::create('ActionNextStepManually', _t('Order.MANUAL_STATUS_CHANGE', '3. Move Order Along')),
660
                        LiteralField::create('OrderStepNextStepHeaderExtra', '<p>'._t('Order.NEEDTOREFRESH', 'Once you have made any changes to the order then you will have to refresh below or save it to move it along.').'</p>'),
661
                        EcommerceCMSButtonField::create(
662
                            'StatusIDExplanation',
663
                            $this->CMSEditLink(),
664
                            _t('Order.REFRESH', 'refresh now')
665
                        )
666
                    )
667
                );
668
            }
669
        }
670
        $fields->addFieldsToTab(
671
            'Root.Next',
672
            $nextFieldArray
673
        );
674
675
        $this->MyStep()->addOrderStepFields($fields, $this);
676
677
        if ($submitted) {
678
            $permaLinkLabel = _t('Order.PERMANENT_LINK', 'Customer Link');
679
            $html = '<p>'.$permaLinkLabel.': <a href="'.$this->getRetrieveLink().'">'.$this->getRetrieveLink().'</a></p>';
680
            $shareLinkLabel = _t('Order.SHARE_LINK', 'Share Link');
681
            $html .= '<p>'.$shareLinkLabel.': <a href="'.$this->getShareLink().'">'.$this->getShareLink().'</a></p>';
682
            $feedbackLinkLabel = _t('Order.FEEDBACK_LINK', 'Feedback Link');
683
            $html .= '<p>'.$feedbackLinkLabel.': <a href="'.$this->getFeedbackLink().'">'.$this->getFeedbackLink().'</a></p>';
684
            $js = "window.open(this.href, 'payment', 'toolbar=0,scrollbars=1,location=1,statusbar=1,menubar=0,resizable=1,width=800,height=600'); return false;";
685
            $link = $this->getPrintLink();
686
            $label = _t('Order.PRINT_INVOICE', 'invoice');
687
            $linkHTML = '<a href="'.$link.'" onclick="'.$js.'">'.$label.'</a>';
688
            $linkHTML .= ' | ';
689
            $link = $this->getPackingSlipLink();
690
            $label = _t('Order.PRINT_PACKING_SLIP', 'packing slip');
691
            $labelPrint = _t('Order.PRINT', 'Print');
692
            $linkHTML .= '<a href="'.$link.'" onclick="'.$js.'">'.$label.'</a>';
693
            $html .= '<h3>';
694
            $html .= $labelPrint.': '.$linkHTML;
695
            $html .= '</h3>';
696
697
            $fields->addFieldToTab(
698
                'Root.Main',
699
                LiteralField::create('getPrintLinkANDgetPackingSlipLink', $html)
700
            );
701
702
            //add order here as well.
703
            $fields->addFieldToTab(
704
                'Root.Main',
705
                new LiteralField(
706
                    'MainDetails',
707
                    '<iframe src="'.$this->getPrintLink().'" width="100%" height="2500" style="border: 5px solid #2e7ead; border-radius: 2px;"></iframe>'
708
                )
709
            );
710
            $fields->addFieldsToTab(
711
                'Root.Items',
712
                array(
713
                    GridField::create(
714
                        'Items_Sold',
715
                        'Items Sold',
716
                        $this->Items(),
717
                        new GridFieldConfig_RecordViewer
718
                    )
719
                )
720
            );
721
            $fields->addFieldsToTab(
722
                'Root.Extras',
723
                array(
724
                    GridField::create(
725
                        'Modifications',
726
                        'Price (and other) adjustments',
727
                        $this->Modifiers(),
728
                        new GridFieldConfig_RecordViewer
729
                    )
730
                )
731
            );
732
            $fields->addFieldsToTab(
733
                'Root.Emails',
734
                array(
735
                    $this->getEmailsTableField()
736
                )
737
            );
738
            $fields->addFieldsToTab(
739
                'Root.Payments',
740
                array(
741
                    $this->getPaymentsField(),
742
                    new ReadOnlyField('TotalPaidNice', _t('Order.TOTALPAID', 'Total Paid'), $this->TotalPaidAsCurrencyObject()->Nice()),
743
                    new ReadOnlyField('TotalOutstandingNice', _t('Order.TOTALOUTSTANDING', 'Total Outstanding'), $this->getTotalOutstandingAsMoney()->Nice())
744
                )
745
            );
746
            if ($this->canPay()) {
747
                $link = EcommercePaymentController::make_payment_link($this->ID);
748
                $js = "window.open(this.href, 'payment', 'toolbar=0,scrollbars=1,location=1,statusbar=1,menubar=0,resizable=1,width=800,height=600'); return false;";
749
                $header = _t('Order.MAKEPAYMENT', 'make payment');
750
                $label = _t('Order.MAKEADDITIONALPAYMENTNOW', 'make additional payment now');
751
                $linkHTML = '<a href="'.$link.'" onclick="'.$js.'">'.$label.'</a>';
752
                $fields->addFieldToTab('Root.Payments', new HeaderField('MakeAdditionalPaymentHeader', $header, 3));
753
                $fields->addFieldToTab('Root.Payments', new LiteralField('MakeAdditionalPayment', $linkHTML));
754
            }
755
            //member
756
            $member = $this->Member();
0 ignored issues
show
Bug introduced by
The method Member() does not exist on Order. Did you maybe mean CreateOrReturnExistingMember()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
757
            if ($member && $member->exists()) {
758
                $fields->addFieldToTab('Root.Account', new LiteralField('MemberDetails', $member->getEcommerceFieldsForCMS()));
759
            } else {
760
                $fields->addFieldToTab('Root.Account', new LiteralField(
761
                    'MemberDetails',
762
                    '<p>'._t('Order.NO_ACCOUNT', 'There is no --- account --- associated with this order').'</p>'
763
                ));
764
            }
765
            if ($this->getFeedbackLink()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->getFeedbackLink() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
766
                $fields->addFieldToTab(
767
                    'Root.Account',
768
                    GridField::create(
769
                        'OrderFeedback',
770
                        Injector::inst()->get('OrderFeedback')->singular_name(),
771
                        OrderFeedback::get()->filter(array('OrderID' => $this->ID)),
772
                        GridFieldConfig_RecordViewer::create()
773
                    )
774
                );
775
            }
776
            $cancelledField = $fields->dataFieldByName('CancelledByID');
777
            $fields->removeByName('CancelledByID');
778
            $shopAdminAndCurrentCustomerArray = EcommerceRole::list_of_admins(true);
779
            if ($member && $member->exists()) {
780
                $shopAdminAndCurrentCustomerArray[$member->ID] = $member->getName();
781
            }
782
            if ($this->CancelledByID) {
0 ignored issues
show
Documentation introduced by
The property CancelledByID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
783
                if ($cancellingMember = $this->CancelledBy()) {
0 ignored issues
show
Documentation Bug introduced by
The method CancelledBy does not exist on object<Order>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
784
                    $shopAdminAndCurrentCustomerArray[$this->CancelledByID] = $cancellingMember->getName();
0 ignored issues
show
Documentation introduced by
The property CancelledByID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
785
                }
786
            }
787
            if ($this->canCancel()) {
788
                $fields->addFieldsToTab(
789
                    'Root.Cancellations',
790
                    array(
791
                        DropdownField::create(
792
                            'CancelledByID',
793
                            $cancelledField->Title(),
794
                            $shopAdminAndCurrentCustomerArray
795
                        )
796
                    )
797
                );
798
            } else {
799
                $cancelledBy = isset($shopAdminAndCurrentCustomerArray[$this->CancelledByID]) && $this->CancelledByID ? $shopAdminAndCurrentCustomerArray[$this->CancelledByID] : _t('Order.NOT_CANCELLED', 'not cancelled');
0 ignored issues
show
Documentation introduced by
The property CancelledByID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
800
                $fields->addFieldsToTab(
801
                    'Root.Cancellations',
802
                    ReadonlyField::create(
803
                        'CancelledByDisplay',
804
                        $cancelledField->Title(),
805
                        $cancelledBy
806
807
                    )
808
                );
809
            }
810
            $fields->addFieldToTab('Root.Log', $this->getOrderStatusLogsTableField_Archived());
811
            $submissionLog = $this->SubmissionLog();
812
            if ($submissionLog) {
813
                $fields->addFieldToTab(
814
                    'Root.Log',
815
                    ReadonlyField::create(
816
                        'SequentialOrderNumber',
817
                        _t('Order.SEQUENTIALORDERNUMBER', 'Consecutive order number'),
818
                        $submissionLog->SequentialOrderNumber
819
                    )->setRightTitle('e.g. 1,2,3,4,5...')
820
                );
821
            }
822
        } else {
823
            $linkText = _t(
824
                'Order.LOAD_THIS_ORDER',
825
                'load this order'
826
            );
827
            $message = _t(
828
                'Order.NOSUBMITTEDYET',
829
                'No details are shown here as this order has not been submitted yet. You can {link} to submit it... NOTE: For this, you will be logged in as the customer and logged out as (shop)admin .',
830
                array('link' => '<a href="'.$this->getRetrieveLink().'" data-popup="true">'.$linkText.'</a>')
0 ignored issues
show
Documentation introduced by
array('link' => '<a href...' . $linkText . '</a>') is of type array<string,string,{"link":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
831
            );
832
            $fields->addFieldToTab('Root.Next', new LiteralField('MainDetails', '<p>'.$message.'</p>'));
833
            $fields->addFieldToTab('Root.Items', $this->getOrderItemsField());
834
            $fields->addFieldToTab('Root.Extras', $this->getModifierTableField());
835
836
            //MEMBER STUFF
837
            $specialOptionsArray = array();
838
            if ($this->MemberID) {
0 ignored issues
show
Documentation introduced by
The property MemberID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
839
                $specialOptionsArray[0] = _t('Order.SELECTCUSTOMER', '--- Remover Customer ---');
840
                $specialOptionsArray[$this->MemberID] = _t('Order.LEAVEWITHCURRENTCUSTOMER', '- Leave with current customer: ').$this->Member()->getTitle();
0 ignored issues
show
Documentation introduced by
The property MemberID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Bug introduced by
The method Member() does not exist on Order. Did you maybe mean CreateOrReturnExistingMember()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
841
            } elseif ($currentMember) {
842
                $specialOptionsArray[0] = _t('Order.SELECTCUSTOMER', '--- Select Customers ---');
843
                $currentMemberID = $currentMember->ID;
844
                $specialOptionsArray[$currentMemberID] = _t('Order.ASSIGNTHISORDERTOME', '- Assign this order to me: ').$currentMember->getTitle();
845
            }
846
            //MEMBER FIELD!!!!!!!
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
847
            $memberArray = $specialOptionsArray + EcommerceRole::list_of_customers(true);
848
            $fields->addFieldToTab('Root.Next', new DropdownField('MemberID', _t('Order.SELECTCUSTOMER', 'Select Customer'), $memberArray), 'CustomerOrderNote');
849
            $memberArray = null;
0 ignored issues
show
Unused Code introduced by
$memberArray is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
850
        }
851
        $fields->addFieldToTab('Root.Addresses', new HeaderField('BillingAddressHeader', _t('Order.BILLINGADDRESS', 'Billing Address')));
852
853
        $fields->addFieldToTab('Root.Addresses', $this->getBillingAddressField());
854
855
        if (EcommerceConfig::get('OrderAddress', 'use_separate_shipping_address')) {
856
            $fields->addFieldToTab('Root.Addresses', new HeaderField('ShippingAddressHeader', _t('Order.SHIPPINGADDRESS', 'Shipping Address')));
857
            $fields->addFieldToTab('Root.Addresses', new CheckboxField('UseShippingAddress', _t('Order.USESEPERATEADDRESS', 'Use separate shipping address?')));
858
            if ($this->UseShippingAddress) {
0 ignored issues
show
Documentation introduced by
The property UseShippingAddress does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
859
                $fields->addFieldToTab('Root.Addresses', $this->getShippingAddressField());
860
            }
861
        }
862
        $currencies = EcommerceCurrency::get_list();
863
        if ($currencies && $currencies->count()) {
864
            $currencies = $currencies->map()->toArray();
865
            $fields->addFieldToTab('Root.Currency', new ReadOnlyField('ExchangeRate ', _t('Order.EXCHANGERATE', 'Exchange Rate'), $this->ExchangeRate));
0 ignored issues
show
Documentation introduced by
The property ExchangeRate does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
866
            $fields->addFieldToTab('Root.Currency', $currencyField = new DropdownField('CurrencyUsedID', _t('Order.CurrencyUsed', 'Currency Used'), $currencies));
867
            if ($this->IsSubmitted()) {
868
                $fields->replaceField('CurrencyUsedID', $fields->dataFieldByName('CurrencyUsedID')->performReadonlyTransformation());
869
            }
870
        } else {
871
            $fields->addFieldToTab('Root.Currency', new LiteralField('CurrencyInfo', '<p>You can not change currencies, because no currencies have been created.</p>'));
872
            $fields->replaceField('CurrencyUsedID', $fields->dataFieldByName('CurrencyUsedID')->performReadonlyTransformation());
873
        }
874
        $fields->addFieldToTab('Root.Log', new ReadonlyField('Created', _t('Root.CREATED', 'Created')));
875
        $fields->addFieldToTab('Root.Log', new ReadonlyField('LastEdited', _t('Root.LASTEDITED', 'Last saved')));
876
        $this->extend('updateCMSFields', $fields);
877
878
        return $fields;
879
    }
880
881
    /**
882
     * Field to add and edit Order Items.
883
     *
884
     * @return GridField
885
     */
886
    protected function getOrderItemsField()
887
    {
888
        $gridFieldConfig = GridFieldConfigForOrderItems::create();
889
        $source = $this->OrderItems();
890
891
        return new GridField('OrderItems', _t('OrderItems.PLURALNAME', 'Order Items'), $source, $gridFieldConfig);
892
    }
893
894
    /**
895
     * Field to add and edit Modifiers.
896
     *
897
     * @return GridField
898
     */
899
    public function getModifierTableField()
900
    {
901
        $gridFieldConfig = GridFieldConfigForOrderItems::create();
902
        $source = $this->Modifiers();
903
904
        return new GridField('OrderModifiers', _t('OrderItems.PLURALNAME', 'Order Items'), $source, $gridFieldConfig);
905
    }
906
907
    /**
908
     *@return GridField
909
     **/
910
    protected function getBillingAddressField()
911
    {
912
        $this->CreateOrReturnExistingAddress('BillingAddress');
913
        $gridFieldConfig = GridFieldConfig::create()->addComponents(
914
            new GridFieldToolbarHeader(),
915
            new GridFieldSortableHeader(),
916
            new GridFieldDataColumns(),
917
            new GridFieldPaginator(10),
918
            new GridFieldEditButton(),
919
            new GridFieldDetailForm()
920
        );
921
        //$source = $this->BillingAddress();
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
922
        $source = BillingAddress::get()->filter(array('OrderID' => $this->ID));
923
924
        return new GridField('BillingAddress', _t('BillingAddress.SINGULARNAME', 'Billing Address'), $source, $gridFieldConfig);
925
    }
926
927
    /**
928
     *@return GridField
929
     **/
930
    protected function getShippingAddressField()
931
    {
932
        $this->CreateOrReturnExistingAddress('ShippingAddress');
933
        $gridFieldConfig = GridFieldConfig::create()->addComponents(
934
            new GridFieldToolbarHeader(),
935
            new GridFieldSortableHeader(),
936
            new GridFieldDataColumns(),
937
            new GridFieldPaginator(10),
938
            new GridFieldEditButton(),
939
            new GridFieldDetailForm()
940
        );
941
        //$source = $this->ShippingAddress();
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
942
        $source = ShippingAddress::get()->filter(array('OrderID' => $this->ID));
943
944
        return new GridField('ShippingAddress', _t('BillingAddress.SINGULARNAME', 'Shipping Address'), $source, $gridFieldConfig);
945
    }
946
947
    /**
948
     * Needs to be public because the OrderStep::getCMSFIelds accesses it.
949
     *
950
     * @param string    $sourceClass
951
     * @param string    $title
952
     *
953
     * @return GridField
954
     **/
955
    public function getOrderStatusLogsTableField(
956
        $sourceClass = 'OrderStatusLog',
957
        $title = ''
958
    ) {
959
        $gridFieldConfig = GridFieldConfig_RecordViewer::create()->addComponents(
960
            new GridFieldAddNewButton('toolbar-header-right'),
961
            new GridFieldDetailForm()
962
        );
963
        $title ? $title : $title = _t('OrderStatusLog.PLURALNAME', 'Order Status Logs');
964
        $source = $this->OrderStatusLogs()->Filter(array('ClassName' => $sourceClass));
0 ignored issues
show
Bug introduced by
The method OrderStatusLogs() does not exist on Order. Did you maybe mean getOrderStatusLogsTableField()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
965
        $gf = new GridField($sourceClass, $title, $source, $gridFieldConfig);
966
        $gf->setModelClass($sourceClass);
967
968
        return $gf;
969
    }
970
971
    /**
972
     * Needs to be public because the OrderStep::getCMSFIelds accesses it.
973
     *
974
     * @param string    $sourceClass
975
     * @param string    $title
976
     *
977
     * @return GridField
978
     **/
979
    public function getOrderStatusLogsTableFieldEditable(
980
        $sourceClass = 'OrderStatusLog',
981
        $title = ''
982
    ) {
983
        $gf = $this->getOrderStatusLogsTableField($sourceClass, $title);
984
        $gf->getConfig()->addComponents(
985
            new GridFieldEditButton()
986
        );
987
        return $gf;
988
    }
989
990
    /**
991
     * @param string    $sourceClass
992
     * @param string    $title
993
     * @param FieldList $fieldList          (Optional)
0 ignored issues
show
Documentation introduced by
Should the type for parameter $fieldList not be null|FieldList?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
994
     * @param FieldList $detailedFormFields (Optional)
0 ignored issues
show
Documentation introduced by
Should the type for parameter $detailedFormFields not be null|FieldList?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
995
     *
996
     * @return GridField
997
     **/
998
    protected function getOrderStatusLogsTableField_Archived(
999
        $sourceClass = 'OrderStatusLog',
1000
        $title = '',
1001
        FieldList $fieldList = null,
0 ignored issues
show
Unused Code introduced by
The parameter $fieldList is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1002
        FieldList $detailedFormFields = null
0 ignored issues
show
Unused Code introduced by
The parameter $detailedFormFields is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1003
    ) {
1004
        $title ? $title : $title = _t('OrderLog.PLURALNAME', 'Order Log');
1005
        $source = $this->OrderStatusLogs();
0 ignored issues
show
Bug introduced by
The method OrderStatusLogs() does not exist on Order. Did you maybe mean getOrderStatusLogsTableField()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1006
        if ($sourceClass != 'OrderStatusLog' && class_exists($sourceClass)) {
1007
            $source = $source->filter(array('ClassName' => ClassInfo::subclassesFor($sourceClass)));
1008
        }
1009
        $gridField = GridField::create($sourceClass, $title, $source, $config = GridFieldConfig_RelationEditor::create());
1010
        $config->removeComponentsByType('GridFieldAddExistingAutocompleter');
1011
        $config->removeComponentsByType('GridFieldDeleteAction');
1012
1013
        return $gridField;
1014
    }
1015
1016
    /**
1017
     * @return GridField
1018
     **/
1019
    public function getEmailsTableField()
1020
    {
1021
        $gridFieldConfig = GridFieldConfig_RecordViewer::create()->addComponents(
1022
            new GridFieldDetailForm()
1023
        );
1024
1025
        return new GridField('Emails', _t('Order.CUSTOMER_EMAILS', 'Customer Emails'), $this->Emails(), $gridFieldConfig);
0 ignored issues
show
Bug introduced by
The method Emails() does not exist on Order. Did you maybe mean getEmailsTableField()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1026
    }
1027
1028
    /**
1029
     * @return GridField
1030
     */
1031
    protected function getPaymentsField()
1032
    {
1033
        $gridFieldConfig = GridFieldConfig_RecordViewer::create()->addComponents(
1034
            new GridFieldDetailForm(),
1035
            new GridFieldEditButton()
1036
        );
1037
1038
        return new GridField('Payments', _t('Order.PAYMENTS', 'Payments'), $this->Payments(), $gridFieldConfig);
0 ignored issues
show
Bug introduced by
The method Payments() does not exist on Order. Did you maybe mean getPaymentsField()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1039
    }
1040
1041
    /**
1042
     * @return OrderStepField
1043
     */
1044
    public function OrderStepField()
1045
    {
1046
        return OrderStepField::create($name = 'MyOrderStep', $this, Member::currentUser());
1047
    }
1048
1049
    /*******************************************************
1050
       * 2. MAIN TRANSITION FUNCTIONS
1051
    *******************************************************/
1052
1053
    /**
1054
     * init runs on start of a new Order (@see onAfterWrite)
1055
     * it adds all the modifiers to the orders and the starting OrderStep.
1056
     *
1057
     * @param bool $recalculate
1058
     *
1059
     * @return DataObject (Order)
1060
     **/
1061
    public function init($recalculate = false)
1062
    {
1063
        if ($this->IsSubmitted()) {
1064
            user_error('Can not init an order that has been submitted', E_USER_NOTICE);
1065
        } else {
1066
            //to do: check if shop is open....
1067
            if ($this->StatusID || $recalculate) {
0 ignored issues
show
Documentation introduced by
The property StatusID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1068
                if (!$this->StatusID) {
0 ignored issues
show
Documentation introduced by
The property StatusID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1069
                    $createdOrderStatus = DataObject::get_one('OrderStep');
1070
                    if (!$createdOrderStatus) {
1071
                        user_error('No ordersteps have been created', E_USER_WARNING);
1072
                    }
1073
                    $this->StatusID = $createdOrderStatus->ID;
0 ignored issues
show
Documentation introduced by
The property StatusID does not exist on object<Order>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1074
                }
1075
                $createdModifiersClassNames = array();
1076
                $modifiersAsArrayList = new ArrayList();
1077
                $modifiers = $this->modifiersFromDatabase($includingRemoved = true);
0 ignored issues
show
Documentation introduced by
$includingRemoved = true is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1078
                if ($modifiers->count()) {
1079
                    foreach ($modifiers as $modifier) {
1080
                        $modifiersAsArrayList->push($modifier);
1081
                    }
1082
                }
1083
                if ($modifiersAsArrayList->count()) {
1084
                    foreach ($modifiersAsArrayList as $modifier) {
1085
                        $createdModifiersClassNames[$modifier->ID] = $modifier->ClassName;
1086
                    }
1087
                } else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
1088
                }
1089
                $modifiersToAdd = EcommerceConfig::get('Order', 'modifiers');
1090
                if (is_array($modifiersToAdd) && count($modifiersToAdd) > 0) {
1091
                    foreach ($modifiersToAdd as $numericKey => $className) {
1092
                        if (!in_array($className, $createdModifiersClassNames)) {
1093
                            if (class_exists($className)) {
1094
                                $modifier = new $className();
1095
                                //only add the ones that should be added automatically
1096
                                if (!$modifier->DoNotAddAutomatically()) {
1097
                                    if (is_a($modifier, 'OrderModifier')) {
1098
                                        $modifier->OrderID = $this->ID;
1099
                                        $modifier->Sort = $numericKey;
1100
                                        //init method includes a WRITE
1101
                                        $modifier->init();
1102
                                        //IMPORTANT - add as has_many relationship  (Attributes can be a modifier OR an OrderItem)
1103
                                        $this->Attributes()->add($modifier);
0 ignored issues
show
Bug introduced by
The method Attributes() does not exist on Order. Did you maybe mean getOrderAttributesByType()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1104
                                        $modifiersAsArrayList->push($modifier);
1105
                                    }
1106
                                }
1107
                            } else {
1108
                                user_error('reference to a non-existing class: '.$className.' in modifiers', E_USER_NOTICE);
1109
                            }
1110
                        }
1111
                    }
1112
                }
1113
                $this->extend('onInit', $this);
1114
                //careful - this will call "onAfterWrite" again
1115
                $this->write();
1116
            }
1117
        }
1118
1119
        return $this;
1120
    }
1121
1122
    /**
1123
     * @var array
1124
     */
1125
    private static $_try_to_finalise_order_is_running = array();
1126
1127
    /**
1128
     * Goes through the order steps and tries to "apply" the next status to the order.
1129
     *
1130
     * @param bool $runAgain
1131
     * @param bool $fromOrderQueue - is it being called from the OrderProcessQueue (or similar)
1132
     *
1133
     * @return null
1134
     **/
1135
    public function tryToFinaliseOrder($runAgain = false, $fromOrderQueue = false)
1136
    {
1137
        if (empty(self::$_try_to_finalise_order_is_running[$this->ID]) || $runAgain) {
1138
            self::$_try_to_finalise_order_is_running[$this->ID] = true;
1139
1140
            //if the order has been cancelled then we do not process it ...
1141
            if ($this->CancelledByID) {
0 ignored issues
show
Documentation introduced by
The property CancelledByID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1142
                $this->Archive(true);
1143
1144
                return;
1145
            }
1146
            // if it is in the queue it has to run from the queue tasks
1147
            // if it ruins from the queue tasks then it has to be one currently processing.
1148
            $queueObjectSingleton = Injector::inst()->get('OrderProcessQueue');
1149
            if ($myQueueObject = $queueObjectSingleton->getQueueObject($this)) {
1150
                if ($fromOrderQueue) {
1151
                    if (! $myQueueObject->InProcess) {
1152
                        return;
1153
                    }
1154
                } else {
1155
                    return;
1156
                }
1157
            }
1158
            //a little hack to make sure we do not rely on a stored value
1159
            //of "isSubmitted"
1160
            $this->_isSubmittedTempVar = -1;
0 ignored issues
show
Documentation Bug introduced by
The property $_isSubmittedTempVar was declared of type boolean, but -1 is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
1161
            //status of order is being progressed
1162
            $nextStatusID = $this->doNextStatus();
1163
            if ($nextStatusID) {
1164
                $nextStatusObject = OrderStep::get()->byID($nextStatusID);
1165
                if ($nextStatusObject) {
1166
                    $delay = $nextStatusObject->CalculatedDeferTimeInSeconds($this);
1167
                    if ($delay > 0) {
1168
                        //adjust delay time from seconds since being submitted
1169
                        if ($nextStatusObject->DeferFromSubmitTime) {
1170
                            $delay = $delay - $this->SecondsSinceBeingSubmitted();
1171
                            if ($delay < 0) {
1172
                                $delay = 0;
1173
                            }
1174
                        }
1175
                        $queueObjectSingleton->AddOrderToQueue(
1176
                            $this,
1177
                            $delay
1178
                        );
1179
                    } else {
1180
                        //status has been completed, so it can be released
1181
                        self::$_try_to_finalise_order_is_running[$this->ID] = false;
1182
                        $this->tryToFinaliseOrder($runAgain, $fromOrderQueue);
1183
                    }
1184
                }
1185
            }
1186
            self::$_try_to_finalise_order_is_running[$this->ID] = false;
1187
        }
1188
    }
1189
1190
    /**
1191
     * Goes through the order steps and tries to "apply" the next step
1192
     * Step is updated after the other one is completed...
1193
     *
1194
     * @return int (StatusID or false if the next status can not be "applied")
1195
     **/
1196
    public function doNextStatus()
1197
    {
1198
        if ($this->MyStep()->initStep($this)) {
1199
            if ($this->MyStep()->doStep($this)) {
1200
                if ($nextOrderStepObject = $this->MyStep()->nextStep($this)) {
1201
                    $this->StatusID = $nextOrderStepObject->ID;
0 ignored issues
show
Documentation introduced by
The property StatusID does not exist on object<Order>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1202
                    $this->write();
1203
1204
                    return $this->StatusID;
0 ignored issues
show
Documentation introduced by
The property StatusID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1205
                }
1206
            }
1207
        }
1208
1209
        return 0;
1210
    }
1211
1212
    /**
1213
     * cancel an order.
1214
     *
1215
     * @param Member $member - (optional) the user cancelling the order
0 ignored issues
show
Documentation introduced by
Should the type for parameter $member not be Member|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1216
     * @param string $reason - (optional) the reason the order is cancelled
1217
     *
1218
     * @return OrderStatusLog_Cancel
1219
     */
1220
    public function Cancel($member = null, $reason = '')
1221
    {
1222
        if ($member && $member instanceof Member) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
1223
            //we have a valid member
1224
        } else {
1225
            $member = EcommerceRole::get_default_shop_admin_user();
1226
        }
1227
        if ($member) {
1228
            //archive and write
1229
            $this->Archive($avoidWrites = true);
1230
            if ($avoidWrites) {
1231
                DB::query('Update "Order" SET CancelledByID = '.$member->ID.' WHERE ID = '.$this->ID.' LIMIT 1;');
1232
            } else {
1233
                $this->CancelledByID = $member->ID;
0 ignored issues
show
Documentation introduced by
The property CancelledByID does not exist on object<Order>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1234
                $this->write();
1235
            }
1236
            //create log ...
1237
            $log = OrderStatusLog_Cancel::create();
1238
            $log->AuthorID = $member->ID;
0 ignored issues
show
Documentation introduced by
The property AuthorID does not exist on object<OrderStatusLog_Cancel>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1239
            $log->OrderID = $this->ID;
0 ignored issues
show
Documentation introduced by
The property OrderID does not exist on object<OrderStatusLog_Cancel>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1240
            $log->Note = $reason;
0 ignored issues
show
Documentation introduced by
The property Note does not exist on object<OrderStatusLog_Cancel>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1241
            if ($member->IsShopAdmin()) {
1242
                $log->InternalUseOnly = true;
0 ignored issues
show
Documentation introduced by
The property InternalUseOnly does not exist on object<OrderStatusLog_Cancel>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1243
            }
1244
            $log->write();
1245
            //remove from queue ...
1246
            $queueObjectSingleton = Injector::inst()->get('OrderProcessQueue');
1247
            $ordersinQueue = $queueObjectSingleton->removeOrderFromQueue($this);
0 ignored issues
show
Unused Code introduced by
$ordersinQueue is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1248
            $this->extend('doCancel', $member, $log);
1249
1250
            return $log;
1251
        }
1252
    }
1253
1254
    /**
1255
     * returns true if successful.
1256
     *
1257
     * @param bool $avoidWrites
1258
     *
1259
     * @return bool
1260
     */
1261
    public function Archive($avoidWrites = true)
1262
    {
1263
        $lastOrderStep = OrderStep::last_order_step();
1264
        if ($lastOrderStep) {
1265
            if ($avoidWrites) {
1266
                DB::query('
1267
                    UPDATE "Order"
1268
                    SET "Order"."StatusID" = '.$lastOrderStep->ID.'
1269
                    WHERE "Order"."ID" = '.$this->ID.'
1270
                    LIMIT 1
1271
                ');
1272
1273
                return true;
1274
            } else {
1275
                $this->StatusID = $lastOrderStep->ID;
0 ignored issues
show
Documentation introduced by
The property StatusID does not exist on object<Order>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1276
                $this->write();
1277
1278
                return true;
1279
            }
1280
        }
1281
1282
        return false;
1283
    }
1284
1285
    /*******************************************************
1286
       * 3. STATUS RELATED FUNCTIONS / SHORTCUTS
1287
    *******************************************************/
1288
1289
    /**
1290
     * Avoids caching of $this->Status().
1291
     *
1292
     * @return DataObject (current OrderStep)
1293
     */
1294
    public function MyStep()
1295
    {
1296
        $step = null;
1297
        if ($this->StatusID) {
0 ignored issues
show
Documentation introduced by
The property StatusID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1298
            $step = OrderStep::get()->byID($this->StatusID);
0 ignored issues
show
Documentation introduced by
The property StatusID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1299
        }
1300
        if (! $step) {
1301
            $step = DataObject::get_one(
1302
                'OrderStep',
1303
                null,
1304
                $cacheDataObjectGetOne = false
1305
            );
1306
        }
1307
        if (! $step) {
1308
            $step = OrderStep_Created::create();
1309
        }
1310
        if (! $step) {
1311
            user_error('You need an order step in your Database.');
1312
        }
1313
1314
        return $step;
1315
    }
1316
1317
    /**
1318
     * Return the OrderStatusLog that is relevant to the Order status.
1319
     *
1320
     * @return OrderStatusLog
1321
     */
1322
    public function RelevantLogEntry()
1323
    {
1324
        return $this->MyStep()->RelevantLogEntry($this);
1325
    }
1326
1327
    /**
1328
     * @return OrderStep (current OrderStep that can be seen by customer)
1329
     */
1330
    public function CurrentStepVisibleToCustomer()
1331
    {
1332
        $obj = $this->MyStep();
1333
        if ($obj->HideStepFromCustomer) {
1334
            $obj = OrderStep::get()->where('"OrderStep"."Sort" < '.$obj->Sort.' AND "HideStepFromCustomer" = 0')->Last();
1335
            if (!$obj) {
1336
                $obj = DataObject::get_one('OrderStep');
1337
            }
1338
        }
1339
1340
        return $obj;
1341
    }
1342
1343
    /**
1344
     * works out if the order is still at the first OrderStep.
1345
     *
1346
     * @return bool
1347
     */
1348
    public function IsFirstStep()
1349
    {
1350
        $firstStep = DataObject::get_one('OrderStep');
1351
        $currentStep = $this->MyStep();
1352
        if ($firstStep && $currentStep) {
1353
            if ($firstStep->ID == $currentStep->ID) {
1354
                return true;
1355
            }
1356
        }
1357
1358
        return false;
1359
    }
1360
1361
    /**
1362
     * Is the order still being "edited" by the customer?
1363
     *
1364
     * @return bool
1365
     */
1366
    public function IsInCart()
1367
    {
1368
        return (bool) $this->IsSubmitted() ? false : true;
1369
    }
1370
1371
    /**
1372
     * The order has "passed" the IsInCart phase.
1373
     *
1374
     * @return bool
1375
     */
1376
    public function IsPastCart()
1377
    {
1378
        return (bool) $this->IsInCart() ? false : true;
1379
    }
1380
1381
    /**
1382
     * Are there still steps the order needs to go through?
1383
     *
1384
     * @return bool
1385
     */
1386
    public function IsUncomplete()
1387
    {
1388
        return (bool) $this->MyStep()->ShowAsUncompletedOrder;
1389
    }
1390
1391
    /**
1392
     * Is the order in the :"processing" phaase.?
1393
     *
1394
     * @return bool
1395
     */
1396
    public function IsProcessing()
1397
    {
1398
        return (bool) $this->MyStep()->ShowAsInProcessOrder;
1399
    }
1400
1401
    /**
1402
     * Is the order completed?
1403
     *
1404
     * @return bool
1405
     */
1406
    public function IsCompleted()
1407
    {
1408
        return (bool) $this->MyStep()->ShowAsCompletedOrder;
1409
    }
1410
1411
    /**
1412
     * Has the order been paid?
1413
     * TODO: why do we check if there is a total at all?
1414
     *
1415
     * @return bool
1416
     */
1417
    public function IsPaid()
1418
    {
1419
        if ($this->IsSubmitted()) {
1420
            return (bool) (($this->Total() >= 0) && ($this->TotalOutstanding() <= 0));
1421
        }
1422
1423
        return false;
1424
    }
1425
1426
    /**
1427
     * @alias for getIsPaidNice
1428
     * @return string
1429
     */
1430
    public function IsPaidNice()
1431
    {
1432
        return $this->getIsPaidNice();
1433
    }
1434
1435
1436
    public function getIsPaidNice()
1437
    {
1438
        return $this->IsPaid() ? 'yes' : 'no';
1439
    }
1440
1441
1442
    /**
1443
     * Has the order been paid?
1444
     * TODO: why do we check if there is a total at all?
1445
     *
1446
     * @return bool
1447
     */
1448
    public function PaymentIsPending()
1449
    {
1450
        if ($this->IsSubmitted()) {
1451
            if ($this->IsPaid()) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
1452
                //do nothing;
1453
            } elseif (($payments = $this->Payments()) && $payments->count()) {
0 ignored issues
show
Bug introduced by
The method Payments() does not exist on Order. Did you maybe mean getPaymentsField()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1454
                foreach ($payments as $payment) {
1455
                    if ('Pending' == $payment->Status) {
1456
                        return true;
1457
                    }
1458
                }
1459
            }
1460
        }
1461
1462
        return false;
1463
    }
1464
1465
    /**
1466
     * shows payments that are meaningfull
1467
     * if the order has been paid then only show successful payments.
1468
     *
1469
     * @return DataList
1470
     */
1471
    public function RelevantPayments()
1472
    {
1473
        if ($this->IsPaid()) {
1474
            return $this->Payments("\"Status\" = 'Success'");
0 ignored issues
show
Bug introduced by
The method Payments() does not exist on Order. Did you maybe mean getPaymentsField()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1475
        //EcommercePayment::get()->
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1476
            //	filter(array("OrderID" => $this->ID, "Status" => "Success"));
0 ignored issues
show
Unused Code Comprehensibility introduced by
64% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1477
        } else {
1478
            return $this->Payments();
0 ignored issues
show
Bug introduced by
The method Payments() does not exist on Order. Did you maybe mean getPaymentsField()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1479
        }
1480
    }
1481
1482
1483
    /**
1484
     * Has the order been cancelled?
1485
     *
1486
     * @return bool
1487
     */
1488
    public function IsCancelled()
1489
    {
1490
        return $this->getIsCancelled();
1491
    }
1492
    public function getIsCancelled()
1493
    {
1494
        return $this->CancelledByID ? true : false;
0 ignored issues
show
Documentation introduced by
The property CancelledByID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1495
    }
1496
1497
    /**
1498
     * Has the order been cancelled by the customer?
1499
     *
1500
     * @return bool
1501
     */
1502
    public function IsCustomerCancelled()
1503
    {
1504
        if ($this->MemberID > 0 && $this->MemberID == $this->IsCancelledID) {
0 ignored issues
show
Documentation introduced by
The property MemberID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Documentation introduced by
The property IsCancelledID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Unused Code introduced by
This if statement, and the following return statement can be replaced with return $this->MemberID >...= $this->IsCancelledID;.
Loading history...
1505
            return true;
1506
        }
1507
1508
        return false;
1509
    }
1510
1511
    /**
1512
     * Has the order been cancelled by the  administrator?
1513
     *
1514
     * @return bool
1515
     */
1516
    public function IsAdminCancelled()
1517
    {
1518
        if ($this->IsCancelled()) {
1519
            if (!$this->IsCustomerCancelled()) {
1520
                $admin = Member::get()->byID($this->CancelledByID);
0 ignored issues
show
Documentation introduced by
The property CancelledByID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1521
                if ($admin) {
1522
                    if ($admin->IsShopAdmin()) {
1523
                        return true;
1524
                    }
1525
                }
1526
            }
1527
        }
1528
1529
        return false;
1530
    }
1531
1532
    /**
1533
     * Is the Shop Closed for business?
1534
     *
1535
     * @return bool
1536
     */
1537
    public function ShopClosed()
1538
    {
1539
        return EcomConfig()->ShopClosed;
1540
    }
1541
1542
    /*******************************************************
1543
       * 4. LINKING ORDER WITH MEMBER AND ADDRESS
1544
    *******************************************************/
1545
1546
    /**
1547
     * Returns a member linked to the order.
1548
     * If a member is already linked, it will return the existing member.
1549
     * Otherwise it will return a new Member.
1550
     *
1551
     * Any new member is NOT written, because we dont want to create a new member unless we have to!
1552
     * We will not add a member to the order unless a new one is created in the checkout
1553
     * OR the member is logged in / logs in.
1554
     *
1555
     * Also note that if a new member is created, it is not automatically written
1556
     *
1557
     * @param bool $forceCreation - if set to true then the member will always be saved in the database.
1558
     *
1559
     * @return Member
1560
     **/
1561
    public function CreateOrReturnExistingMember($forceCreation = false)
1562
    {
1563
        if ($this->IsSubmitted()) {
1564
            return $this->Member();
0 ignored issues
show
Bug introduced by
The method Member() does not exist on Order. Did you maybe mean CreateOrReturnExistingMember()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1565
        }
1566
        if ($this->MemberID) {
0 ignored issues
show
Documentation introduced by
The property MemberID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1567
            $member = $this->Member();
0 ignored issues
show
Bug introduced by
The method Member() does not exist on Order. Did you maybe mean CreateOrReturnExistingMember()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
Unused Code introduced by
$member is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1568
        } elseif ($member = Member::currentUser()) {
1569
            if (!$member->IsShopAdmin()) {
1570
                $this->MemberID = $member->ID;
0 ignored issues
show
Documentation introduced by
The property MemberID does not exist on object<Order>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1571
                $this->write();
1572
            }
1573
        }
1574
        $member = $this->Member();
0 ignored issues
show
Bug introduced by
The method Member() does not exist on Order. Did you maybe mean CreateOrReturnExistingMember()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1575
        if (!$member) {
1576
            $member = new Member();
1577
        }
1578
        if ($member && $forceCreation) {
1579
            $member->write();
1580
        }
1581
1582
        return $member;
1583
    }
1584
1585
    /**
1586
     * Returns either the existing one or a new Order Address...
1587
     * All Orders will have a Shipping and Billing address attached to it.
1588
     * Method used to retrieve object e.g. for $order->BillingAddress(); "BillingAddress" is the method name you can use.
1589
     * If the method name is the same as the class name then dont worry about providing one.
1590
     *
1591
     * @param string $className             - ClassName of the Address (e.g. BillingAddress or ShippingAddress)
1592
     * @param string $alternativeMethodName - method to retrieve Address
1593
     **/
1594
    public function CreateOrReturnExistingAddress($className = 'BillingAddress', $alternativeMethodName = '')
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
1595
    {
1596
        if ($this->exists()) {
1597
            $methodName = $className;
1598
            if ($alternativeMethodName) {
1599
                $methodName = $alternativeMethodName;
1600
            }
1601
            if ($this->IsSubmitted()) {
1602
                return $this->$methodName();
1603
            }
1604
            $variableName = $className.'ID';
1605
            $address = null;
1606
            if ($this->$variableName) {
1607
                $address = $this->$methodName();
1608
            }
1609
            if (!$address) {
1610
                $address = new $className();
1611
                if ($member = $this->CreateOrReturnExistingMember()) {
1612
                    if ($member->exists()) {
1613
                        $address->FillWithLastAddressFromMember($member, $write = false);
1614
                    }
1615
                }
1616
            }
1617
            if ($address) {
1618
                if (!$address->exists()) {
1619
                    $address->write();
1620
                }
1621
                if ($address->OrderID != $this->ID) {
1622
                    $address->OrderID = $this->ID;
1623
                    $address->write();
1624
                }
1625
                if ($this->$variableName != $address->ID) {
1626
                    if (!$this->IsSubmitted()) {
1627
                        $this->$variableName = $address->ID;
1628
                        $this->write();
1629
                    }
1630
                }
1631
1632
                return $address;
1633
            }
1634
        }
1635
1636
        return;
1637
    }
1638
1639
    /**
1640
     * Sets the country in the billing and shipping address.
1641
     *
1642
     * @param string $countryCode            - code for the country e.g. NZ
1643
     * @param bool   $includeBillingAddress
1644
     * @param bool   $includeShippingAddress
1645
     **/
1646
    public function SetCountryFields($countryCode, $includeBillingAddress = true, $includeShippingAddress = true)
1647
    {
1648
        if ($this->IsSubmitted()) {
1649
            user_error('Can not change country in submitted order', E_USER_NOTICE);
1650
        } else {
1651
            if ($includeBillingAddress) {
1652
                if ($billingAddress = $this->CreateOrReturnExistingAddress('BillingAddress')) {
1653
                    $billingAddress->SetCountryFields($countryCode);
1654
                }
1655
            }
1656
            if (EcommerceConfig::get('OrderAddress', 'use_separate_shipping_address')) {
1657
                if ($includeShippingAddress) {
1658
                    if ($shippingAddress = $this->CreateOrReturnExistingAddress('ShippingAddress')) {
1659
                        $shippingAddress->SetCountryFields($countryCode);
1660
                    }
1661
                }
1662
            }
1663
        }
1664
    }
1665
1666
    /**
1667
     * Sets the region in the billing and shipping address.
1668
     *
1669
     * @param int $regionID - ID for the region to be set
1670
     **/
1671
    public function SetRegionFields($regionID)
1672
    {
1673
        if ($this->IsSubmitted()) {
1674
            user_error('Can not change country in submitted order', E_USER_NOTICE);
1675
        } else {
1676
            if ($billingAddress = $this->CreateOrReturnExistingAddress('BillingAddress')) {
1677
                $billingAddress->SetRegionFields($regionID);
1678
            }
1679
            if ($this->CanHaveShippingAddress()) {
1680
                if ($shippingAddress = $this->CreateOrReturnExistingAddress('ShippingAddress')) {
1681
                    $shippingAddress->SetRegionFields($regionID);
1682
                }
1683
            }
1684
        }
1685
    }
1686
1687
    /**
1688
     * Stores the preferred currency of the order.
1689
     * IMPORTANTLY we store the exchange rate for future reference...
1690
     *
1691
     * @param EcommerceCurrency $currency
0 ignored issues
show
Documentation introduced by
There is no parameter named $currency. Did you maybe mean $newCurrency?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
1692
     */
1693
    public function UpdateCurrency($newCurrency)
1694
    {
1695
        if ($this->IsSubmitted()) {
1696
            user_error('Can not set the currency after the order has been submitted', E_USER_NOTICE);
1697
        } else {
1698
            if (! is_a($newCurrency, Object::getCustomClass('EcommerceCurrency'))) {
1699
                $newCurrency = EcommerceCurrency::default_currency();
1700
            }
1701
            $this->CurrencyUsedID = $newCurrency->ID;
0 ignored issues
show
Documentation introduced by
The property CurrencyUsedID does not exist on object<Order>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1702
            $this->ExchangeRate = $newCurrency->getExchangeRate();
0 ignored issues
show
Documentation introduced by
The property ExchangeRate does not exist on object<Order>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1703
            $this->write();
1704
        }
1705
    }
1706
1707
    /**
1708
     * alias for UpdateCurrency.
1709
     *
1710
     * @param EcommerceCurrency $currency
1711
     */
1712
    public function SetCurrency($currency)
1713
    {
1714
        $this->UpdateCurrency($currency);
1715
    }
1716
1717
    /*******************************************************
1718
       * 5. CUSTOMER COMMUNICATION
1719
    *******************************************************/
1720
1721
    /**
1722
     * Send the invoice of the order by email.
1723
     *
1724
     * @param string $emailClassName     (optional) class used to send email
1725
     * @param string $subject            (optional) subject for the email
1726
     * @param string $message            (optional) the main message in the email
1727
     * @param bool   $resend             (optional) send the email even if it has been sent before
1728
     * @param bool   $adminOnlyOrToEmail (optional) sends the email to the ADMIN ONLY, if you provide an email, it will go to the email...
1729
     *
1730
     * @return bool TRUE on success, FALSE on failure
1731
     */
1732
    public function sendEmail(
1733
        $emailClassName = 'Order_InvoiceEmail',
1734
        $subject = '',
1735
        $message = '',
1736
        $resend = false,
1737
        $adminOnlyOrToEmail = false
1738
    ) {
1739
        return $this->prepareAndSendEmail(
1740
            $emailClassName,
1741
            $subject,
1742
            $message,
1743
            $resend,
1744
            $adminOnlyOrToEmail
1745
        );
1746
    }
1747
1748
    /**
1749
     * Sends a message to the shop admin ONLY and not to the customer
1750
     * This can be used by ordersteps and orderlogs to notify the admin of any potential problems.
1751
     *
1752
     * @param string         $emailClassName       - (optional) template to be used ...
1753
     * @param string         $subject              - (optional) subject for the email
1754
     * @param string         $message              - (optional) message to be added with the email
1755
     * @param bool           $resend               - (optional) can it be sent twice?
1756
     * @param bool | string  $adminOnlyOrToEmail   - (optional) sends the email to the ADMIN ONLY, if you provide an email, it will go to the email...
1757
     *
1758
     * @return bool TRUE for success, FALSE for failure (not tested)
1759
     */
1760
    public function sendAdminNotification(
1761
        $emailClassName = 'Order_ErrorEmail',
1762
        $subject = '',
1763
        $message = '',
1764
        $resend = false,
1765
        $adminOnlyOrToEmail = true
1766
    ) {
1767
        return $this->prepareAndSendEmail(
1768
            $emailClassName,
1769
            $subject,
1770
            $message,
1771
            $resend,
1772
            $adminOnlyOrToEmail
1773
        );
1774
    }
1775
1776
    /**
1777
     * returns the order formatted as an email.
1778
     *
1779
     * @param string $emailClassName - template to use.
1780
     * @param string $subject        - (optional) the subject (which can be used as title in email)
1781
     * @param string $message        - (optional) the additional message
1782
     *
1783
     * @return string (html)
1784
     */
1785
    public function renderOrderInEmailFormat(
1786
        $emailClassName,
1787
        $subject = '',
1788
        $message = ''
1789
    ) {
1790
        $arrayData = $this->createReplacementArrayForEmail($subject, $message);
1791
        Config::nest();
1792
        Config::inst()->update('SSViewer', 'theme_enabled', true);
1793
        $html = $arrayData->renderWith($emailClassName);
1794
        Config::unnest();
1795
1796
        return Order_Email::emogrify_html($html);
1797
    }
1798
1799
    /**
1800
     * Send a mail of the order to the client (and another to the admin).
1801
     *
1802
     * @param string         $emailClassName       - (optional) template to be used ...
1803
     * @param string         $subject              - (optional) subject for the email
1804
     * @param string         $message              - (optional) message to be added with the email
1805
     * @param bool           $resend               - (optional) can it be sent twice?
1806
     * @param bool | string  $adminOnlyOrToEmail   - (optional) sends the email to the ADMIN ONLY, if you provide an email, it will go to the email...
1807
     *
1808
     * @return bool TRUE for success, FALSE for failure (not tested)
1809
     */
1810
    protected function prepareAndSendEmail(
1811
        $emailClassName = 'Order_InvoiceEmail',
1812
        $subject,
0 ignored issues
show
Coding Style introduced by
Parameters which have default values should be placed at the end.

If you place a parameter with a default value before a parameter with a default value, the default value of the first parameter will never be used as it will always need to be passed anyway:

// $a must always be passed; it's default value is never used.
function someFunction($a = 5, $b) { }
Loading history...
1813
        $message,
1814
        $resend = false,
1815
        $adminOnlyOrToEmail = false
1816
    ) {
1817
        $arrayData = $this->createReplacementArrayForEmail($subject, $message);
1818
        $from = Order_Email::get_from_email();
1819
        //why are we using this email and NOT the member.EMAIL?
1820
        //for historical reasons????
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1821
        if ($adminOnlyOrToEmail) {
1822
            if (filter_var($adminOnlyOrToEmail, FILTER_VALIDATE_EMAIL)) {
1823
                $to = $adminOnlyOrToEmail;
1824
            // invalid e-mail address
1825
            } else {
1826
                $to = Order_Email::get_from_email();
1827
            }
1828
        } else {
1829
            $to = $this->getOrderEmail();
1830
        }
1831
        if ($from && $to) {
1832
            if (! class_exists($emailClassName)) {
1833
                user_error('Invalid Email ClassName provided: '. $emailClassName, E_USER_ERROR);
1834
            }
1835
            $email = new $emailClassName();
1836
            if (!(is_a($email, Object::getCustomClass('Email')))) {
1837
                user_error('No correct email class provided.', E_USER_ERROR);
1838
            }
1839
            $email->setFrom($from);
1840
            $email->setTo($to);
1841
            //we take the subject from the Array Data, just in case it has been adjusted.
1842
            $email->setSubject($arrayData->getField('Subject'));
1843
            //we also see if a CC and a BCC have been added
1844
            ;
1845
            if ($cc = $arrayData->getField('CC')) {
1846
                $email->setCc($cc);
1847
            }
1848
            if ($bcc = $arrayData->getField('BCC')) {
1849
                $email->setBcc($bcc);
1850
            }
1851
            $email->populateTemplate($arrayData);
1852
            // This might be called from within the CMS,
1853
            // so we need to restore the theme, just in case
1854
            // templates within the theme exist
1855
            Config::nest();
1856
            Config::inst()->update('SSViewer', 'theme_enabled', true);
1857
            $email->setOrder($this);
1858
            $email->setResend($resend);
1859
            $result = $email->send(null);
1860
            Config::unnest();
1861
            if (Director::isDev()) {
1862
                return true;
1863
            } else {
1864
                return $result;
1865
            }
1866
        }
1867
1868
        return false;
1869
    }
1870
1871
    /**
1872
     * returns the Data that can be used in the body of an order Email
1873
     * we add the subject here so that the subject, for example, can be added to the <title>
1874
     * of the email template.
1875
     * we add the subject here so that the subject, for example, can be added to the <title>
1876
     * of the email template.
1877
     *
1878
     * @param string $subject  - (optional) subject for email
1879
     * @param string $message  - (optional) the additional message
1880
     *
1881
     * @return ArrayData
1882
     *                   - Subject - EmailSubject
1883
     *                   - Message - specific message for this order
1884
     *                   - Message - custom message
1885
     *                   - OrderStepMessage - generic message for step
1886
     *                   - Order
1887
     *                   - EmailLogo
1888
     *                   - ShopPhysicalAddress
1889
     *                   - CurrentDateAndTime
1890
     *                   - BaseURL
1891
     *                   - CC
1892
     *                   - BCC
1893
     */
1894
    protected function createReplacementArrayForEmail($subject = '', $message = '')
1895
    {
1896
        $step = $this->MyStep();
1897
        $config = $this->EcomConfig();
1898
        $replacementArray = array();
1899
        //set subject
1900
        if (! $subject) {
1901
            $subject = $step->CalculatedEmailSubject($this);
1902
        }
1903
        if (! $message) {
1904
            $message = $step->CalculatedCustomerMessage($this);
1905
        }
1906
        $subject = str_replace('[OrderNumber]', $this->ID, $subject);
1907
        //set other variables
1908
        $replacementArray['Subject'] = $subject;
1909
        $replacementArray['To'] = '';
1910
        $replacementArray['CC'] = '';
1911
        $replacementArray['BCC'] = '';
1912
        $replacementArray['OrderStepMessage'] = $message;
1913
        $replacementArray['Order'] = $this;
1914
        $replacementArray['EmailLogo'] = $config->EmailLogo();
0 ignored issues
show
Documentation Bug introduced by
The method EmailLogo does not exist on object<EcommerceDBConfig>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1915
        $replacementArray['ShopPhysicalAddress'] = $config->ShopPhysicalAddress;
0 ignored issues
show
Documentation introduced by
The property ShopPhysicalAddress does not exist on object<EcommerceDBConfig>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1916
        $replacementArray['CurrentDateAndTime'] = DBField::create_field('SS_Datetime', 'Now');
1917
        $replacementArray['BaseURL'] = Director::baseURL();
1918
        $arrayData = ArrayData::create($replacementArray);
1919
        $this->extend('updateReplacementArrayForEmail', $arrayData);
1920
1921
        return $arrayData;
1922
    }
1923
1924
    /*******************************************************
1925
       * 6. ITEM MANAGEMENT
1926
    *******************************************************/
1927
1928
    /**
1929
     * returns a list of Order Attributes by type.
1930
     *
1931
     * @param array | String $types
1932
     *
1933
     * @return ArrayList
1934
     */
1935
    public function getOrderAttributesByType($types)
1936
    {
1937
        if (!is_array($types) && is_string($types)) {
1938
            $types = array($types);
1939
        }
1940
        if (!is_array($al)) {
0 ignored issues
show
Bug introduced by
The variable $al seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
1941
            user_error('wrong parameter (types) provided in Order::getOrderAttributesByTypes');
1942
        }
1943
        $al = new ArrayList();
1944
        $items = $this->Items();
1945
        foreach ($items as $item) {
1946
            if (in_array($item->OrderAttributeType(), $types)) {
1947
                $al->push($item);
1948
            }
1949
        }
1950
        $modifiers = $this->Modifiers();
1951
        foreach ($modifiers as $modifier) {
1952
            if (in_array($modifier->OrderAttributeType(), $types)) {
1953
                $al->push($modifier);
1954
            }
1955
        }
1956
1957
        return $al;
1958
    }
1959
1960
    /**
1961
     * Returns the items of the order.
1962
     * Items are the order items (products) and NOT the modifiers (discount, tax, etc...).
1963
     *
1964
     * N. B. this method returns Order Items
1965
     * also see Buaybles
1966
1967
     *
1968
     * @param string filter - where statement to exclude certain items OR ClassName (e.g. 'TaxModifier')
1969
     *
1970
     * @return DataList (OrderItems)
1971
     */
1972
    public function Items($filterOrClassName = '')
1973
    {
1974
        if (!$this->exists()) {
1975
            $this->write();
1976
        }
1977
1978
        return $this->itemsFromDatabase($filterOrClassName);
1979
    }
1980
1981
    /**
1982
     * @alias function of Items
1983
     *
1984
     * N. B. this method returns Order Items
1985
     * also see Buaybles
1986
     *
1987
     * @param string filter - where statement to exclude certain items.
1988
     * @alias for Items
1989
     * @return DataList (OrderItems)
1990
     */
1991
    public function OrderItems($filterOrClassName = '')
1992
    {
1993
        return $this->Items($filterOrClassName);
1994
    }
1995
1996
    /**
1997
     * returns the buyables asscoiated with the order items.
1998
     *
1999
     * NB. this method retursn buyables
2000
     *
2001
     * @param string filter - where statement to exclude certain items.
2002
     *
2003
     * @return ArrayList (Buyables)
2004
     */
2005
    public function Buyables($filterOrClassName = '')
2006
    {
2007
        $items = $this->Items($filterOrClassName);
2008
        $arrayList = new ArrayList();
2009
        foreach ($items as $item) {
2010
            $arrayList->push($item->Buyable());
2011
        }
2012
2013
        return $arrayList;
2014
    }
2015
2016
    /**
2017
     * Return all the {@link OrderItem} instances that are
2018
     * available as records in the database.
2019
     *
2020
     * @param string filter - where statement to exclude certain items,
2021
     *   you can also pass a classname (e.g. MyOrderItem), in which case only this class will be returned (and any class extending your given class)
2022
     *
2023
     * @return DataList (OrderItems)
2024
     */
2025
    protected function itemsFromDatabase($filterOrClassName = '')
2026
    {
2027
        $className = 'OrderItem';
2028
        $extrafilter = '';
2029
        if ($filterOrClassName) {
2030
            if (class_exists($filterOrClassName)) {
2031
                $className = $filterOrClassName;
2032
            } else {
2033
                $extrafilter = " AND $filterOrClassName";
2034
            }
2035
        }
2036
2037
        return $className::get()->filter(array('OrderID' => $this->ID))->where($extrafilter);
2038
    }
2039
2040
    /**
2041
     * @alias for Modifiers
2042
     *
2043
     * @return DataList (OrderModifiers)
2044
     */
2045
    public function OrderModifiers()
2046
    {
2047
        return $this->Modifiers();
2048
    }
2049
2050
    /**
2051
     * Returns the modifiers of the order, if it hasn't been saved yet
2052
     * it returns the modifiers from session, if it has, it returns them
2053
     * from the DB entry. ONLY USE OUTSIDE ORDER.
2054
     *
2055
     * @param string filter - where statement to exclude certain items OR ClassName (e.g. 'TaxModifier')
2056
     *
2057
     * @return DataList (OrderModifiers)
2058
     */
2059
    public function Modifiers($filterOrClassName = '')
2060
    {
2061
        return $this->modifiersFromDatabase($filterOrClassName);
2062
    }
2063
2064
    /**
2065
     * Get all {@link OrderModifier} instances that are
2066
     * available as records in the database.
2067
     * NOTE: includes REMOVED Modifiers, so that they do not get added again...
2068
     *
2069
     * @param string filter - where statement to exclude certain items OR ClassName (e.g. 'TaxModifier')
2070
     *
2071
     * @return DataList (OrderModifiers)
2072
     */
2073
    protected function modifiersFromDatabase($filterOrClassName = '')
2074
    {
2075
        $className = 'OrderModifier';
2076
        $extrafilter = '';
2077
        if ($filterOrClassName) {
2078
            if (class_exists($filterOrClassName)) {
2079
                $className = $filterOrClassName;
2080
            } else {
2081
                $extrafilter = " AND $filterOrClassName";
2082
            }
2083
        }
2084
2085
        return $className::get()->where('"OrderAttribute"."OrderID" = '.$this->ID." $extrafilter");
2086
    }
2087
2088
    /**
2089
     * Calculates and updates all the order attributes.
2090
     *
2091
     * @param bool $recalculate - run it, even if it has run already
2092
     */
2093
    public function calculateOrderAttributes($recalculate = false)
2094
    {
2095
        if ($this->IsSubmitted()) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
2096
            //submitted orders are NEVER recalculated.
2097
            //they are set in stone.
2098
        } elseif (self::get_needs_recalculating($this->ID) || $recalculate) {
2099
            if ($this->StatusID || $this->TotalItems()) {
0 ignored issues
show
Documentation introduced by
The property StatusID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2100
                $this->ensureCorrectExchangeRate();
2101
                $this->calculateOrderItems($recalculate);
2102
                $this->calculateModifiers($recalculate);
2103
                $this->extend('onCalculateOrder');
2104
            }
2105
        }
2106
    }
2107
2108
    /**
2109
     * Calculates and updates all the product items.
2110
     *
2111
     * @param bool $recalculate - run it, even if it has run already
2112
     */
2113
    protected function calculateOrderItems($recalculate = false)
2114
    {
2115
        //check if order has modifiers already
2116
        //check /re-add all non-removable ones
2117
        //$start = microtime();
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
2118
        $orderItems = $this->itemsFromDatabase();
2119
        if ($orderItems->count()) {
2120
            foreach ($orderItems as $orderItem) {
2121
                if ($orderItem) {
2122
                    $orderItem->runUpdate($recalculate);
2123
                }
2124
            }
2125
        }
2126
        $this->extend('onCalculateOrderItems', $orderItems);
2127
    }
2128
2129
    /**
2130
     * Calculates and updates all the modifiers.
2131
     *
2132
     * @param bool $recalculate - run it, even if it has run already
2133
     */
2134
    protected function calculateModifiers($recalculate = false)
2135
    {
2136
        $createdModifiers = $this->modifiersFromDatabase();
2137
        if ($createdModifiers->count()) {
2138
            foreach ($createdModifiers as $modifier) {
2139
                if ($modifier) {
2140
                    $modifier->runUpdate($recalculate);
2141
                }
2142
            }
2143
        }
2144
        $this->extend('onCalculateModifiers', $createdModifiers);
2145
    }
2146
2147
    /**
2148
     * Returns the subtotal of the modifiers for this order.
2149
     * If a modifier appears in the excludedModifiers array, it is not counted.
2150
     *
2151
     * @param string|array $excluded               - Class(es) of modifier(s) to ignore in the calculation.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $excluded not be string|array|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
2152
     * @param bool         $stopAtExcludedModifier - when this flag is TRUE, we stop adding the modifiers when we reach an excluded modifier.
2153
     *
2154
     * @return float
0 ignored issues
show
Documentation introduced by
Should the return type not be integer?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
2155
     */
2156
    public function ModifiersSubTotal($excluded = null, $stopAtExcludedModifier = false)
2157
    {
2158
        $total = 0;
2159
        $modifiers = $this->Modifiers();
2160
        if ($modifiers->count()) {
2161
            foreach ($modifiers as $modifier) {
2162
                if (!$modifier->IsRemoved()) { //we just double-check this...
2163
                    if (is_array($excluded) && in_array($modifier->ClassName, $excluded)) {
2164
                        if ($stopAtExcludedModifier) {
2165
                            break;
2166
                        }
2167
                        //do the next modifier
2168
                        continue;
2169
                    } elseif (is_string($excluded) && ($modifier->ClassName == $excluded)) {
2170
                        if ($stopAtExcludedModifier) {
2171
                            break;
2172
                        }
2173
                        //do the next modifier
2174
                        continue;
2175
                    }
2176
                    $total += $modifier->CalculationTotal();
2177
                }
2178
            }
2179
        }
2180
2181
        return $total;
2182
    }
2183
2184
    /**
2185
     * returns a modifier that is an instanceof the classname
2186
     * it extends.
2187
     *
2188
     * @param string $className: class name for the modifier
0 ignored issues
show
Documentation introduced by
There is no parameter named $className:. Did you maybe mean $className?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
2189
     *
2190
     * @return DataObject (OrderModifier)
2191
     **/
2192
    public function RetrieveModifier($className)
2193
    {
2194
        $modifiers = $this->Modifiers();
2195
        if ($modifiers->count()) {
2196
            foreach ($modifiers as $modifier) {
2197
                if (is_a($modifier, Object::getCustomClass($className))) {
2198
                    return $modifier;
2199
                }
2200
            }
2201
        }
2202
    }
2203
2204
    /*******************************************************
2205
       * 7. CRUD METHODS (e.g. canView, canEdit, canDelete, etc...)
2206
    *******************************************************/
2207
2208
    /**
2209
     * @param Member $member
2210
     *
2211
     * @return DataObject (Member)
2212
     **/
2213
    //TODO: please comment why we make use of this function
2214
    protected function getMemberForCanFunctions(Member $member = null)
2215
    {
2216
        if (!$member) {
2217
            $member = Member::currentUser();
2218
        }
2219
        if (!$member) {
2220
            $member = new Member();
2221
            $member->ID = 0;
2222
        }
2223
2224
        return $member;
2225
    }
2226
2227
    /**
2228
     * @param Member $member
0 ignored issues
show
Documentation introduced by
Should the type for parameter $member not be Member|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
2229
     *
2230
     * @return bool
2231
     **/
2232
    public function canCreate($member = null)
2233
    {
2234
        $member = $this->getMemberForCanFunctions($member);
2235
        $extended = $this->extendedCan(__FUNCTION__, $member);
0 ignored issues
show
Documentation introduced by
$member is of type object<DataObject>, but the function expects a object<Member>|integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2236
        if ($extended !== null) {
2237
            return $extended;
2238
        }
2239
        if ($member->exists()) {
2240
            return $member->IsShopAdmin();
2241
        }
2242
    }
2243
2244
    /**
2245
     * Standard SS method - can the current member view this order?
2246
     *
2247
     * @param Member $member
0 ignored issues
show
Documentation introduced by
Should the type for parameter $member not be Member|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
2248
     *
2249
     * @return bool
2250
     **/
2251
    public function canView($member = null)
2252
    {
2253
        if (!$this->exists()) {
2254
            return true;
2255
        }
2256
        $member = $this->getMemberForCanFunctions($member);
2257
        //check if this has been "altered" in any DataExtension
2258
        $extended = $this->extendedCan(__FUNCTION__, $member);
0 ignored issues
show
Documentation introduced by
$member is of type object<DataObject>, but the function expects a object<Member>|integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2259
        if ($extended !== null) {
2260
            return $extended;
2261
        }
2262
        //is the member is a shop admin they can always view it
2263
        if (EcommerceRole::current_member_is_shop_admin($member)) {
2264
            return true;
2265
        }
2266
2267
        //is the member is a shop assistant they can always view it
2268
        if (EcommerceRole::current_member_is_shop_assistant($member)) {
2269
            return true;
2270
        }
2271
        //if the current member OWNS the order, (s)he can always view it.
2272
        if ($member->exists() && $this->MemberID == $member->ID) {
0 ignored issues
show
Documentation introduced by
The property MemberID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2273
            return true;
2274
        }
2275
        //it is the current order
2276
        if ($this->IsInSession()) {
2277
            //we do some additional CHECKS for session hackings!
2278
            if ($member->exists() && $this->MemberID) {
0 ignored issues
show
Documentation introduced by
The property MemberID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2279
                //can't view the order of another member!
2280
                //shop admin exemption is already captured.
2281
                //this is always true
2282
                if ($this->MemberID != $member->ID) {
0 ignored issues
show
Documentation introduced by
The property MemberID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2283
                    return false;
2284
                }
2285
            } else {
2286
                //order belongs to someone, but current user is NOT logged in...
2287
                //this is allowed!
2288
                //the reason it is allowed is because we want to be able to
2289
                //add order to non-existing member
2290
                return true;
2291
            }
2292
        }
2293
2294
        return false;
2295
    }
2296
2297
    /**
2298
     * @param Member $member optional
0 ignored issues
show
Documentation introduced by
Should the type for parameter $member not be Member|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
2299
     * @return bool
2300
     */
2301
    public function canOverrideCanView($member = null)
2302
    {
2303
        if ($this->canView($member)) {
2304
            //can view overrides any concerns
2305
            return true;
2306
        } else {
2307
            $tsOrder = strtotime($this->LastEdited);
2308
            $tsNow = time();
2309
            $minutes = EcommerceConfig::get('Order', 'minutes_an_order_can_be_viewed_without_logging_in');
2310
            if ($minutes && ((($tsNow - $tsOrder) / 60) < $minutes)) {
2311
2312
                //has the order been edited recently?
2313
                return true;
2314
            } elseif ($orderStep = $this->MyStep()) {
2315
2316
                // order is being processed ...
2317
                return $orderStep->canOverrideCanViewForOrder($this, $member);
0 ignored issues
show
Bug introduced by
The method canOverrideCanViewForOrder() does not exist on DataObject. Did you maybe mean canView()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
2318
            }
2319
        }
2320
        return false;
2321
    }
2322
2323
    /**
2324
     * @return bool
2325
     */
2326
    public function IsInSession()
2327
    {
2328
        $orderInSession = ShoppingCart::session_order();
2329
2330
        return $orderInSession && $this->ID && $this->ID == $orderInSession->ID;
2331
    }
2332
2333
    /**
2334
     * returns a pseudo random part of the session id.
2335
     *
2336
     * @param int $size
2337
     *
2338
     * @return string
2339
     */
2340
    public function LessSecureSessionID($size = 7, $start = null)
2341
    {
2342
        if (!$start || $start < 0 || $start > (32 - $size)) {
2343
            $start = 0;
2344
        }
2345
2346
        return substr($this->SessionID, $start, $size);
0 ignored issues
show
Documentation introduced by
The property SessionID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2347
    }
2348
    /**
2349
     *
2350
     * @param Member (optional) $member
0 ignored issues
show
Documentation introduced by
Should the type for parameter $member not be optional|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
2351
     *
2352
     * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
2353
     **/
2354
    public function canViewAdminStuff($member = null)
2355
    {
2356
        $member = $this->getMemberForCanFunctions($member);
2357
        $extended = $this->extendedCan(__FUNCTION__, $member);
0 ignored issues
show
Documentation introduced by
$member is of type object<DataObject>, but the function expects a object<Member>|integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2358
        if ($extended !== null) {
2359
            return $extended;
2360
        }
2361
        if (Permission::checkMember($member, Config::inst()->get('EcommerceRole', 'admin_permission_code'))) {
2362
            return true;
2363
        }
2364
    }
2365
2366
    /**
2367
     * if we set canEdit to false then we
2368
     * can not see the child records
2369
     * Basically, you can edit when you can view and canEdit (even as a customer)
2370
     * Or if you are a Shop Admin you can always edit.
2371
     * Otherwise it is false...
2372
     *
2373
     * @param Member $member
0 ignored issues
show
Documentation introduced by
Should the type for parameter $member not be Member|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
2374
     *
2375
     * @return bool
2376
     **/
2377
    public function canEdit($member = null)
2378
    {
2379
        $member = $this->getMemberForCanFunctions($member);
2380
        $extended = $this->extendedCan(__FUNCTION__, $member);
0 ignored issues
show
Documentation introduced by
$member is of type object<DataObject>, but the function expects a object<Member>|integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2381
        if ($extended !== null) {
2382
            return $extended;
2383
        }
2384
        if ($this->canView($member) && $this->MyStep()->CustomerCanEdit) {
0 ignored issues
show
Documentation introduced by
$member is of type object<DataObject>, but the function expects a object<Member>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2385
            return true;
2386
        }
2387
        if (Permission::checkMember($member, Config::inst()->get('EcommerceRole', 'admin_permission_code'))) {
2388
            return true;
2389
        }
2390
        //is the member is a shop assistant they can always view it
2391
        if (EcommerceRole::current_member_is_shop_assistant($member)) {
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return \EcommerceRole::c...hop_assistant($member);.
Loading history...
2392
            return true;
2393
        }
2394
        return false;
2395
    }
2396
2397
    /**
2398
     * is the order ready to go through to the
2399
     * checkout process.
2400
     *
2401
     * This method checks all the order items and order modifiers
2402
     * If any of them need immediate attention then this is done
2403
     * first after which it will go through to the checkout page.
2404
     *
2405
     * @param Member (optional) $member
0 ignored issues
show
Documentation introduced by
Should the type for parameter $member not be null|Member?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
2406
     *
2407
     * @return bool
2408
     **/
2409
    public function canCheckout(Member $member = null)
2410
    {
2411
        $member = $this->getMemberForCanFunctions($member);
2412
        $extended = $this->extendedCan(__FUNCTION__, $member);
0 ignored issues
show
Documentation introduced by
$member is of type object<DataObject>, but the function expects a object<Member>|integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2413
        if ($extended !== null) {
2414
            return $extended;
2415
        }
2416
        $submitErrors = $this->SubmitErrors();
2417
        if ($submitErrors && $submitErrors->count()) {
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return !($submitErrors &...submitErrors->count());.
Loading history...
2418
            return false;
2419
        }
2420
2421
        return true;
2422
    }
2423
2424
    /**
2425
     * Can the order be submitted?
2426
     * this method can be used to stop an order from being submitted
2427
     * due to something not being completed or done.
2428
     *
2429
     * @see Order::SubmitErrors
2430
     *
2431
     * @param Member $member
0 ignored issues
show
Documentation introduced by
Should the type for parameter $member not be null|Member?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
2432
     *
2433
     * @return bool
2434
     **/
2435
    public function canSubmit(Member $member = null)
2436
    {
2437
        $member = $this->getMemberForCanFunctions($member);
2438
        $extended = $this->extendedCan(__FUNCTION__, $member);
0 ignored issues
show
Documentation introduced by
$member is of type object<DataObject>, but the function expects a object<Member>|integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2439
        if ($extended !== null) {
2440
            return $extended;
2441
        }
2442
        if ($this->IsSubmitted()) {
2443
            return false;
2444
        }
2445
        $submitErrors = $this->SubmitErrors();
2446
        if ($submitErrors && $submitErrors->count()) {
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return !($submitErrors &...submitErrors->count());.
Loading history...
2447
            return false;
2448
        }
2449
2450
        return true;
2451
    }
2452
2453
    /**
2454
     * Can a payment be made for this Order?
2455
     *
2456
     * @param Member $member
0 ignored issues
show
Documentation introduced by
Should the type for parameter $member not be null|Member?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
2457
     *
2458
     * @return bool
2459
     **/
2460
    public function canPay(Member $member = null)
2461
    {
2462
        $member = $this->getMemberForCanFunctions($member);
2463
        $extended = $this->extendedCan(__FUNCTION__, $member);
0 ignored issues
show
Documentation introduced by
$member is of type object<DataObject>, but the function expects a object<Member>|integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2464
        if ($extended !== null) {
2465
            return $extended;
2466
        }
2467
        if ($this->IsPaid() || $this->IsCancelled() || $this->PaymentIsPending()) {
2468
            return false;
2469
        }
2470
2471
        return $this->MyStep()->CustomerCanPay;
2472
    }
2473
2474
    /**
2475
     * Can the given member cancel this order?
2476
     *
2477
     * @param Member $member
0 ignored issues
show
Documentation introduced by
Should the type for parameter $member not be null|Member?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
2478
     *
2479
     * @return bool
2480
     **/
2481
    public function canCancel(Member $member = null)
2482
    {
2483
        //if it is already cancelled it can not be cancelled again
2484
        if ($this->CancelledByID) {
0 ignored issues
show
Documentation introduced by
The property CancelledByID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2485
            return false;
2486
        }
2487
        $member = $this->getMemberForCanFunctions($member);
2488
        $extended = $this->extendedCan(__FUNCTION__, $member);
0 ignored issues
show
Documentation introduced by
$member is of type object<DataObject>, but the function expects a object<Member>|integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2489
        if ($extended !== null) {
2490
            return $extended;
2491
        }
2492
        if (EcommerceRole::current_member_can_process_orders($member)) {
2493
            return true;
2494
        }
2495
2496
        return $this->MyStep()->CustomerCanCancel && $this->canView($member);
0 ignored issues
show
Documentation introduced by
$member is of type object<DataObject>, but the function expects a object<Member>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2497
    }
2498
2499
    /**
2500
     * @param Member $member
0 ignored issues
show
Documentation introduced by
Should the type for parameter $member not be Member|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
2501
     *
2502
     * @return bool
2503
     **/
2504
    public function canDelete($member = null)
2505
    {
2506
        $member = $this->getMemberForCanFunctions($member);
2507
        $extended = $this->extendedCan(__FUNCTION__, $member);
0 ignored issues
show
Documentation introduced by
$member is of type object<DataObject>, but the function expects a object<Member>|integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2508
        if ($extended !== null) {
2509
            return $extended;
2510
        }
2511
        if ($this->IsSubmitted()) {
2512
            return false;
2513
        }
2514
        if (Permission::checkMember($member, Config::inst()->get('EcommerceRole', 'admin_permission_code'))) {
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return (bool) \Permissio...min_permission_code'));.
Loading history...
2515
            return true;
2516
        }
2517
2518
        return false;
2519
    }
2520
2521
    /**
2522
     * Returns all the order logs that the current member can view
2523
     * i.e. some order logs can only be viewed by the admin (e.g. suspected fraud orderlog).
2524
     *
2525
     * @return ArrayList (OrderStatusLogs)
2526
     **/
2527
    public function CanViewOrderStatusLogs()
2528
    {
2529
        $canViewOrderStatusLogs = new ArrayList();
2530
        $logs = $this->OrderStatusLogs();
0 ignored issues
show
Bug introduced by
The method OrderStatusLogs() does not exist on Order. Did you maybe mean getOrderStatusLogsTableField()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
2531
        foreach ($logs as $log) {
2532
            if ($log->canView()) {
2533
                $canViewOrderStatusLogs->push($log);
2534
            }
2535
        }
2536
2537
        return $canViewOrderStatusLogs;
2538
    }
2539
2540
    /**
2541
     * returns all the logs that can be viewed by the customer.
2542
     *
2543
     * @return ArrayList (OrderStausLogs)
2544
     */
2545
    public function CustomerViewableOrderStatusLogs()
2546
    {
2547
        $customerViewableOrderStatusLogs = new ArrayList();
2548
        $logs = $this->OrderStatusLogs();
0 ignored issues
show
Bug introduced by
The method OrderStatusLogs() does not exist on Order. Did you maybe mean getOrderStatusLogsTableField()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
2549
        if ($logs) {
2550
            foreach ($logs as $log) {
2551
                if (!$log->InternalUseOnly) {
2552
                    $customerViewableOrderStatusLogs->push($log);
2553
                }
2554
            }
2555
        }
2556
2557
        return $customerViewableOrderStatusLogs;
2558
    }
2559
2560
    /*******************************************************
2561
       * 8. GET METHODS (e.g. Total, SubTotal, Title, etc...)
2562
    *******************************************************/
2563
2564
    /**
2565
     * returns the email to be used for customer communication.
2566
     *
2567
     * @return string
2568
     */
2569
    public function OrderEmail()
2570
    {
2571
        return $this->getOrderEmail();
2572
    }
2573
    public function getOrderEmail()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
2574
    {
2575
        $email = '';
2576
        if ($this->BillingAddressID && $this->BillingAddress()) {
0 ignored issues
show
Documentation introduced by
The property BillingAddressID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Bug introduced by
The method BillingAddress() does not exist on Order. Did you maybe mean getBillingAddressField()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
2577
            $email = $this->BillingAddress()->Email;
0 ignored issues
show
Bug introduced by
The method BillingAddress() does not exist on Order. Did you maybe mean getBillingAddressField()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
2578
        }
2579
        if (! $email) {
2580
            if ($this->MemberID && $this->Member()) {
0 ignored issues
show
Documentation introduced by
The property MemberID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Bug introduced by
The method Member() does not exist on Order. Did you maybe mean CreateOrReturnExistingMember()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
2581
                $email = $this->Member()->Email;
0 ignored issues
show
Bug introduced by
The method Member() does not exist on Order. Did you maybe mean CreateOrReturnExistingMember()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
2582
            }
2583
        }
2584
        $extendedEmail = $this->extend('updateOrderEmail', $email);
2585
        if ($extendedEmail !== null && is_array($extendedEmail) && count($extendedEmail)) {
2586
            $email = implode(';', $extendedEmail);
2587
        }
2588
2589
        return $email;
2590
    }
2591
2592
    /**
2593
     * Returns true if there is a prink or email link.
2594
     *
2595
     * @return bool
2596
     */
2597
    public function HasPrintOrEmailLink()
2598
    {
2599
        return $this->EmailLink() || $this->PrintLink();
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->PrintLink() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
2600
    }
2601
2602
    /**
2603
     * returns the absolute link to the order that can be used in the customer communication (email).
2604
     *
2605
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be false|string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
2606
     */
2607
    public function EmailLink($type = 'Order_StatusEmail')
0 ignored issues
show
Unused Code introduced by
The parameter $type is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2608
    {
2609
        return $this->getEmailLink();
2610
    }
2611
    public function getEmailLink($type = 'Order_StatusEmail')
0 ignored issues
show
Unused Code introduced by
The parameter $type is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Coding Style introduced by
getEmailLink uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
2612
    {
2613
        if (!isset($_REQUEST['print'])) {
2614
            if ($this->IsSubmitted()) {
2615
                return Director::AbsoluteURL(OrderConfirmationPage::get_email_link($this->ID, $this->MyStep()->getEmailClassName(), $actuallySendEmail = true));
2616
            }
2617
        }
2618
    }
2619
2620
    /**
2621
     * returns the absolute link to the order for printing.
2622
     *
2623
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
2624
     */
2625
    public function PrintLink()
2626
    {
2627
        return $this->getPrintLink();
2628
    }
2629
    public function getPrintLink()
0 ignored issues
show
Coding Style introduced by
getPrintLink uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
2630
    {
2631
        if (!isset($_REQUEST['print'])) {
2632
            if ($this->IsSubmitted()) {
2633
                return Director::AbsoluteURL(OrderConfirmationPage::get_order_link($this->ID)).'?print=1';
2634
            }
2635
        }
2636
    }
2637
2638
    /**
2639
     * returns the absolute link to the order for printing.
2640
     *
2641
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
2642
     */
2643
    public function PackingSlipLink()
2644
    {
2645
        return $this->getPackingSlipLink();
2646
    }
2647
    public function getPackingSlipLink()
2648
    {
2649
        if ($this->IsSubmitted()) {
2650
            return Director::AbsoluteURL(OrderConfirmationPage::get_order_link($this->ID)).'?packingslip=1';
2651
        }
2652
    }
2653
2654
    /**
2655
     * returns the absolute link that the customer can use to retrieve the email WITHOUT logging in.
2656
     *
2657
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|false?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
2658
     */
2659
    public function RetrieveLink()
2660
    {
2661
        return $this->getRetrieveLink();
2662
    }
2663
2664
    public function getRetrieveLink()
2665
    {
2666
        //important to recalculate!
2667
        if ($this->IsSubmitted($recalculate = true)) {
2668
            //add session ID if not added yet...
2669
            if (!$this->SessionID) {
0 ignored issues
show
Documentation introduced by
The property SessionID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2670
                $this->write();
2671
            }
2672
2673
            return Director::AbsoluteURL(OrderConfirmationPage::find_link()).'retrieveorder/'.$this->SessionID.'/'.$this->ID.'/';
0 ignored issues
show
Documentation introduced by
The property SessionID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2674
        } else {
2675
            return Director::AbsoluteURL('/shoppingcart/loadorder/'.$this->ID.'/');
2676
        }
2677
    }
2678
2679
    public function ShareLink()
2680
    {
2681
        return $this->getShareLink();
2682
    }
2683
2684
    public function getShareLink()
2685
    {
2686
        $orderItems = $this->itemsFromDatabase();
2687
        $action = 'share';
2688
        $array = array();
2689
        foreach ($orderItems as $orderItem) {
2690
            $array[] = implode(
2691
                ',',
2692
                array(
2693
                    $orderItem->BuyableClassName,
2694
                    $orderItem->BuyableID,
2695
                    $orderItem->Quantity
2696
                )
2697
            );
2698
        }
2699
2700
        return Director::AbsoluteURL(CartPage::find_link($action.'/'.implode('-', $array)));
2701
    }
2702
2703
    /**
2704
     * @alias for getFeedbackLink
2705
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
2706
     */
2707
    public function FeedbackLink()
2708
    {
2709
        return $this->getFeedbackLink();
2710
    }
2711
2712
    /**
2713
     * @return string | null
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
2714
     */
2715
    public function getFeedbackLink()
2716
    {
2717
        $orderConfirmationPage = DataObject::get_one('OrderConfirmationPage');
2718
        if ($orderConfirmationPage->IsFeedbackEnabled) {
2719
            return Director::AbsoluteURL($this->getRetrieveLink()).'#OrderForm_Feedback_FeedbackForm';
0 ignored issues
show
Security Bug introduced by
It seems like $this->getRetrieveLink() targeting Order::getRetrieveLink() can also be of type false; however, Director::absoluteURL() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
2720
        }
2721
    }
2722
2723
    /**
2724
     * link to delete order.
2725
     *
2726
     * @return string
2727
     */
2728
    public function DeleteLink()
2729
    {
2730
        return $this->getDeleteLink();
2731
    }
2732
    public function getDeleteLink()
2733
    {
2734
        if ($this->canDelete()) {
2735
            return ShoppingCart_Controller::delete_order_link($this->ID);
2736
        } else {
2737
            return '';
2738
        }
2739
    }
2740
2741
    /**
2742
     * link to copy order.
2743
     *
2744
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
2745
     */
2746
    public function CopyOrderLink()
2747
    {
2748
        return $this->getCopyOrderLink();
2749
    }
2750
    public function getCopyOrderLink()
2751
    {
2752
        if ($this->canView() && $this->IsSubmitted()) {
2753
            return ShoppingCart_Controller::copy_order_link($this->ID);
2754
        } else {
2755
            return '';
2756
        }
2757
    }
2758
2759
    /**
2760
     * A "Title" for the order, which summarises the main details (date, and customer) in a string.
2761
     *
2762
     * @param string $dateFormat  - e.g. "D j M Y, G:i T"
0 ignored issues
show
Documentation introduced by
Should the type for parameter $dateFormat not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
2763
     * @param bool   $includeName - e.g. by Mr Johnson
2764
     *
2765
     * @return string
2766
     **/
2767
    public function Title($dateFormat = null, $includeName = false)
2768
    {
2769
        return $this->getTitle($dateFormat, $includeName);
2770
    }
2771
    public function getTitle($dateFormat = null, $includeName = false)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
2772
    {
2773
        if ($this->exists()) {
2774
            if ($dateFormat === null) {
2775
                $dateFormat = EcommerceConfig::get('Order', 'date_format_for_title');
2776
            }
2777
            if ($includeName === null) {
2778
                $includeName = EcommerceConfig::get('Order', 'include_customer_name_in_title');
2779
            }
2780
            $title = $this->i18n_singular_name()." #".number_format($this->ID);
2781
            if ($dateFormat) {
2782
                if ($submissionLog = $this->SubmissionLog()) {
2783
                    $dateObject = $submissionLog->dbObject('Created');
2784
                    $placed = _t('Order.PLACED', 'placed');
2785
                } else {
2786
                    $dateObject = $this->dbObject('Created');
2787
                    $placed = _t('Order.STARTED', 'started');
2788
                }
2789
                $title .= ' - '.$placed.' '.$dateObject->Format($dateFormat);
2790
            }
2791
            $name = '';
2792
            if ($this->CancelledByID) {
0 ignored issues
show
Documentation introduced by
The property CancelledByID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2793
                $name = ' - '._t('Order.CANCELLED', 'CANCELLED');
2794
            }
2795
            if ($includeName) {
2796
                $by = _t('Order.BY', 'by');
2797
                if (!$name) {
2798
                    if ($this->BillingAddressID) {
0 ignored issues
show
Documentation introduced by
The property BillingAddressID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2799
                        if ($billingAddress = $this->BillingAddress()) {
0 ignored issues
show
Bug introduced by
The method BillingAddress() does not exist on Order. Did you maybe mean getBillingAddressField()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
2800
                            $name = ' - '.$by.' '.$billingAddress->Prefix.' '.$billingAddress->FirstName.' '.$billingAddress->Surname;
2801
                        }
2802
                    }
2803
                }
2804
                if (!$name) {
2805
                    if ($this->MemberID) {
0 ignored issues
show
Documentation introduced by
The property MemberID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2806
                        if ($member = $this->Member()) {
0 ignored issues
show
Bug introduced by
The method Member() does not exist on Order. Did you maybe mean CreateOrReturnExistingMember()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
2807
                            if ($member->exists()) {
2808
                                if ($memberName = $member->getName()) {
2809
                                    if (!trim($memberName)) {
2810
                                        $memberName = _t('Order.ANONYMOUS', 'anonymous');
2811
                                    }
2812
                                    $name = ' - '.$by.' '.$memberName;
2813
                                }
2814
                            }
2815
                        }
2816
                    }
2817
                }
2818
            }
2819
            $title .= $name;
2820
        } else {
2821
            $title = _t('Order.NEW', 'New').' '.$this->i18n_singular_name();
2822
        }
2823
        $extendedTitle = $this->extend('updateTitle', $title);
2824
        if ($extendedTitle !== null && is_array($extendedTitle) && count($extendedTitle)) {
2825
            $title = implode('; ', $extendedTitle);
2826
        }
2827
2828
        return $title;
2829
    }
2830
2831
    public function OrderItemsSummaryNice()
2832
    {
2833
        return $this->getOrderItemsSummaryNice();
2834
    }
2835
2836
    public function getOrderItemsSummaryNice()
2837
    {
2838
        return DBField::create_field('HTMLText', $this->OrderItemsSummaryAsHTML());
2839
    }
2840
2841
    public function OrderItemsSummaryAsHTML()
2842
    {
2843
        $html = '';
2844
        $x = 0;
2845
        $count = $this->owner->OrderItems()->count();
0 ignored issues
show
Documentation introduced by
The property owner does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2846
        if ($count > 0) {
2847
            $html .= '<ul class="order-items-summary">';
2848
            foreach ($this->owner->OrderItems() as $orderItem) {
0 ignored issues
show
Documentation introduced by
The property owner does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
2849
                $x++;
2850
                $buyable = $orderItem->Buyable();
2851
                $html .= '<li style="font-family: monospace; font-size: 0.9em; color: #1F9433;">- '.$orderItem->Quantity.'x ';
2852
                if ($buyable) {
2853
                    $html .= $buyable->InternalItemID .' '.$buyable->Title;
2854
                } else {
2855
                    $html .= $orderItem->BuyableFullName;
2856
                }
2857
                $html .= '</li>';
2858
                if ($x > 3) {
2859
                    $html .= '<li style="font-family: monospace; font-size: 0.9em; color: black;">- open for more items</li>';
2860
                    break;
2861
                }
2862
            }
2863
            $html .= '</ul>';
2864
        }
2865
        return $html;
2866
    }
2867
2868
    /**
2869
     * Returns the subtotal of the items for this order.
2870
     *
2871
     * @return float
0 ignored issues
show
Documentation introduced by
Should the return type not be integer?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
2872
     */
2873
    public function SubTotal()
2874
    {
2875
        return $this->getSubTotal();
2876
    }
2877
    public function getSubTotal()
2878
    {
2879
        $result = 0;
2880
        $items = $this->Items();
2881
        if ($items->count()) {
2882
            foreach ($items as $item) {
2883
                if (is_a($item, Object::getCustomClass('OrderAttribute'))) {
2884
                    $result += $item->Total();
2885
                }
2886
            }
2887
        }
2888
2889
        return $result;
2890
    }
2891
2892
    /**
2893
     * @return Currency (DB Object)
2894
     **/
2895
    public function SubTotalAsCurrencyObject()
2896
    {
2897
        return DBField::create_field('Currency', $this->SubTotal());
2898
    }
2899
2900
    /**
2901
     * @return Money
2902
     **/
2903
    public function SubTotalAsMoney()
2904
    {
2905
        return $this->getSubTotalAsMoney();
2906
    }
2907
    public function getSubTotalAsMoney()
2908
    {
2909
        return EcommerceCurrency::get_money_object_from_order_currency($this->SubTotal(), $this);
2910
    }
2911
2912
    /**
2913
     * @param string|array $excluded               - Class(es) of modifier(s) to ignore in the calculation.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $excluded not be string|array|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
2914
     * @param bool         $stopAtExcludedModifier - when this flag is TRUE, we stop adding the modifiers when we reach an excluded modifier.
2915
     *
2916
     * @return Currency (DB Object)
2917
     **/
2918
    public function ModifiersSubTotalAsCurrencyObject($excluded = null, $stopAtExcludedModifier = false)
2919
    {
2920
        return DBField::create_field('Currency', $this->ModifiersSubTotal($excluded, $stopAtExcludedModifier));
2921
    }
2922
2923
    /**
2924
     * @param string|array $excluded               - Class(es) of modifier(s) to ignore in the calculation.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $excluded not be string|array|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
2925
     * @param bool         $stopAtExcludedModifier - when this flag is TRUE, we stop adding the modifiers when we reach an excluded modifier.
2926
     *
2927
     * @return Money (DB Object)
2928
     **/
2929
    public function ModifiersSubTotalAsMoneyObject($excluded = null, $stopAtExcludedModifier = false)
2930
    {
2931
        return EcommerceCurrency::get_money_object_from_order_currency($this->ModifiersSubTotal($excluded, $stopAtExcludedModifier), $this);
2932
    }
2933
2934
    /**
2935
     * Returns the total cost of an order including the additional charges or deductions of its modifiers.
2936
     *
2937
     * @return float
0 ignored issues
show
Documentation introduced by
Should the return type not be integer?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
2938
     */
2939
    public function Total()
2940
    {
2941
        return $this->getTotal();
2942
    }
2943
    public function getTotal()
2944
    {
2945
        return $this->SubTotal() + $this->ModifiersSubTotal();
2946
    }
2947
2948
    /**
2949
     * @return Currency (DB Object)
2950
     **/
2951
    public function TotalAsCurrencyObject()
2952
    {
2953
        return DBField::create_field('Currency', $this->Total());
2954
    }
2955
2956
    /**
2957
     * @return Money
2958
     **/
2959
    public function TotalAsMoney()
2960
    {
2961
        return $this->getTotalAsMoney();
2962
    }
2963
    public function getTotalAsMoney()
2964
    {
2965
        return EcommerceCurrency::get_money_object_from_order_currency($this->Total(), $this);
2966
    }
2967
2968
    /**
2969
     * Checks to see if any payments have been made on this order
2970
     * and if so, subracts the payment amount from the order.
2971
     *
2972
     * @return float
0 ignored issues
show
Documentation introduced by
Should the return type not be double|integer?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
2973
     **/
2974
    public function TotalOutstanding()
2975
    {
2976
        return $this->getTotalOutstanding();
2977
    }
2978
    public function getTotalOutstanding()
2979
    {
2980
        if ($this->IsSubmitted()) {
2981
            $total = $this->Total();
2982
            $paid = $this->TotalPaid();
2983
            $outstanding = $total - $paid;
2984
            $maxDifference = EcommerceConfig::get('Order', 'maximum_ignorable_sales_payments_difference');
2985
            if (abs($outstanding) < $maxDifference) {
2986
                $outstanding = 0;
2987
            }
2988
2989
            return floatval($outstanding);
2990
        } else {
2991
            return 0;
2992
        }
2993
    }
2994
2995
    /**
2996
     * @return Currency (DB Object)
2997
     **/
2998
    public function TotalOutstandingAsCurrencyObject()
2999
    {
3000
        return DBField::create_field('Currency', $this->TotalOutstanding());
3001
    }
3002
3003
    /**
3004
     * @return Money
3005
     **/
3006
    public function TotalOutstandingAsMoney()
3007
    {
3008
        return $this->getTotalOutstandingAsMoney();
3009
    }
3010
    public function getTotalOutstandingAsMoney()
3011
    {
3012
        return EcommerceCurrency::get_money_object_from_order_currency($this->TotalOutstanding(), $this);
3013
    }
3014
3015
    /**
3016
     * @return float
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|double?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
3017
     */
3018
    public function TotalPaid()
3019
    {
3020
        return $this->getTotalPaid();
3021
    }
3022
    public function getTotalPaid()
3023
    {
3024
        $paid = 0;
3025
        if ($payments = $this->Payments()) {
0 ignored issues
show
Bug introduced by
The method Payments() does not exist on Order. Did you maybe mean getPaymentsField()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
3026
            foreach ($payments as $payment) {
3027
                if ($payment->Status == 'Success') {
3028
                    $paid += $payment->Amount->getAmount();
3029
                }
3030
            }
3031
        }
3032
        $reverseExchange = 1;
3033
        if ($this->ExchangeRate && $this->ExchangeRate != 1) {
0 ignored issues
show
Documentation introduced by
The property ExchangeRate does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3034
            $reverseExchange = 1 / $this->ExchangeRate;
0 ignored issues
show
Documentation introduced by
The property ExchangeRate does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3035
        }
3036
3037
        return $paid * $reverseExchange;
3038
    }
3039
3040
    /**
3041
     * @return Currency (DB Object)
3042
     **/
3043
    public function TotalPaidAsCurrencyObject()
3044
    {
3045
        return DBField::create_field('Currency', $this->TotalPaid());
3046
    }
3047
3048
    /**
3049
     * @return Money
3050
     **/
3051
    public function TotalPaidAsMoney()
3052
    {
3053
        return $this->getTotalPaidAsMoney();
3054
    }
3055
    public function getTotalPaidAsMoney()
3056
    {
3057
        return EcommerceCurrency::get_money_object_from_order_currency($this->TotalPaid(), $this);
3058
    }
3059
3060
    /**
3061
     * returns the total number of OrderItems (not modifiers).
3062
     * This is meant to run as fast as possible to quickly check
3063
     * if there is anything in the cart.
3064
     *
3065
     * @param bool $recalculate - do we need to recalculate (value is retained during lifetime of Object)
3066
     *
3067
     * @return int
3068
     **/
3069
    public function TotalItems($recalculate = false)
3070
    {
3071
        return $this->getTotalItems($recalculate);
3072
    }
3073
    public function getTotalItems($recalculate = false)
3074
    {
3075
        if ($this->totalItems === null || $recalculate) {
3076
            $this->totalItems = OrderItem::get()
3077
                ->where('"OrderAttribute"."OrderID" = '.$this->ID.' AND "OrderItem"."Quantity" > 0')
3078
                ->count();
3079
        }
3080
3081
        return $this->totalItems;
3082
    }
3083
3084
    /**
3085
     * Little shorthand.
3086
     *
3087
     * @param bool $recalculate
3088
     *
3089
     * @return bool
3090
     **/
3091
    public function MoreThanOneItemInCart($recalculate = false)
3092
    {
3093
        return $this->TotalItems($recalculate) > 1 ? true : false;
3094
    }
3095
3096
    /**
3097
     * returns the total number of OrderItems (not modifiers) times their respectective quantities.
3098
     *
3099
     * @param bool $recalculate - force recalculation
3100
     *
3101
     * @return float
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|double?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
3102
     **/
3103
    public function TotalItemsTimesQuantity($recalculate = false)
3104
    {
3105
        return $this->getTotalItemsTimesQuantity($recalculate);
3106
    }
3107
    public function getTotalItemsTimesQuantity($recalculate = false)
3108
    {
3109
        if ($this->totalItemsTimesQuantity === null || $recalculate) {
3110
            //to do, why do we check if you can edit ????
3111
            $this->totalItemsTimesQuantity = DB::query(
0 ignored issues
show
Documentation Bug introduced by
The property $totalItemsTimesQuantity was declared of type double, but \DB::query(' ...uantity" > 0')->value() is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
3112
                '
3113
                SELECT SUM("OrderItem"."Quantity")
3114
                FROM "OrderItem"
3115
                    INNER JOIN "OrderAttribute" ON "OrderAttribute"."ID" = "OrderItem"."ID"
3116
                WHERE
3117
                    "OrderAttribute"."OrderID" = '.$this->ID.'
3118
                    AND "OrderItem"."Quantity" > 0'
3119
            )->value();
3120
        }
3121
3122
        return $this->totalItemsTimesQuantity - 0;
3123
    }
3124
3125
    /**
3126
     *
3127
     * @return string (country code)
3128
     **/
3129
    public function Country()
3130
    {
3131
        return $this->getCountry();
3132
    }
3133
3134
    /**
3135
    * Returns the country code for the country that applies to the order.
3136
    * @alias  for getCountry
3137
    *
3138
    * @return string - country code e.g. NZ
3139
     */
3140
    public function getCountry()
3141
    {
3142
        $countryCodes = array(
3143
            'Billing' => '',
3144
            'Shipping' => '',
3145
        );
3146
        $code = null;
0 ignored issues
show
Unused Code introduced by
$code is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
3147
        if ($this->BillingAddressID) {
0 ignored issues
show
Documentation introduced by
The property BillingAddressID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3148
            $billingAddress = BillingAddress::get()->byID($this->BillingAddressID);
0 ignored issues
show
Documentation introduced by
The property BillingAddressID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3149
            if ($billingAddress) {
3150
                if ($billingAddress->Country) {
3151
                    $countryCodes['Billing'] = $billingAddress->Country;
3152
                }
3153
            }
3154
        }
3155
        if ($this->IsSeparateShippingAddress()) {
3156
            $shippingAddress = ShippingAddress::get()->byID($this->ShippingAddressID);
0 ignored issues
show
Documentation introduced by
The property ShippingAddressID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3157
            if ($shippingAddress) {
3158
                if ($shippingAddress->ShippingCountry) {
3159
                    $countryCodes['Shipping'] = $shippingAddress->ShippingCountry;
3160
                }
3161
            }
3162
        }
3163
        if (
3164
            (EcommerceConfig::get('OrderAddress', 'use_shipping_address_for_main_region_and_country') && $countryCodes['Shipping'])
3165
            ||
3166
            (!$countryCodes['Billing'] && $countryCodes['Shipping'])
3167
        ) {
3168
            $code = $countryCodes['Shipping'];
3169
        } elseif ($countryCodes['Billing']) {
3170
            $code = $countryCodes['Billing'];
3171
        } else {
3172
            $code = EcommerceCountry::get_country_from_ip();
3173
        }
3174
3175
        return $code;
3176
    }
3177
3178
    /**
3179
     * is this a gift / separate shippingAddress?
3180
     * @return Boolean
3181
     */
3182
    public function IsSeparateShippingAddress()
3183
    {
3184
        return $this->ShippingAddressID && $this->UseShippingAddress;
0 ignored issues
show
Documentation introduced by
The property ShippingAddressID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Documentation introduced by
The property UseShippingAddress does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3185
    }
3186
3187
    /**
3188
     * @alias for getFullNameCountry
3189
     *
3190
     * @return string - country name
3191
     **/
3192
    public function FullNameCountry()
3193
    {
3194
        return $this->getFullNameCountry();
3195
    }
3196
3197
    /**
3198
     * returns name of coutry.
3199
     *
3200
     * @return string - country name
3201
     **/
3202
    public function getFullNameCountry()
3203
    {
3204
        return EcommerceCountry::find_title($this->Country());
3205
    }
3206
3207
    /**
3208
     * @alis for getExpectedCountryName
3209
     * @return string - country name
3210
     **/
3211
    public function ExpectedCountryName()
3212
    {
3213
        return $this->getExpectedCountryName();
3214
    }
3215
3216
    /**
3217
     * returns name of coutry that we expect the customer to have
3218
     * This takes into consideration more than just what has been entered
3219
     * for example, it looks at GEO IP.
3220
     *
3221
     * @todo: why do we dont return a string IF there is only one item.
3222
     *
3223
     * @return string - country name
3224
     **/
3225
    public function getExpectedCountryName()
3226
    {
3227
        return EcommerceCountry::find_title(EcommerceCountry::get_country(false, $this->ID));
3228
    }
3229
3230
    /**
3231
     * return the title of the fixed country (if any).
3232
     *
3233
     * @return string | empty string
3234
     **/
3235
    public function FixedCountry()
3236
    {
3237
        return $this->getFixedCountry();
3238
    }
3239
    public function getFixedCountry()
3240
    {
3241
        $code = EcommerceCountry::get_fixed_country_code();
3242
        if ($code) {
3243
            return EcommerceCountry::find_title($code);
3244
        }
3245
3246
        return '';
3247
    }
3248
3249
    /**
3250
     * Returns the region that applies to the order.
3251
     * we check both billing and shipping, in case one of them is empty.
3252
     *
3253
     * @return DataObject | Null (EcommerceRegion)
0 ignored issues
show
Documentation introduced by
Should the return type not be DataObject|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
3254
     **/
3255
    public function Region()
3256
    {
3257
        return $this->getRegion();
3258
    }
3259
    public function getRegion()
3260
    {
3261
        $regionIDs = array(
3262
            'Billing' => 0,
3263
            'Shipping' => 0,
3264
        );
3265
        if ($this->BillingAddressID) {
0 ignored issues
show
Documentation introduced by
The property BillingAddressID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3266
            if ($billingAddress = $this->BillingAddress()) {
0 ignored issues
show
Bug introduced by
The method BillingAddress() does not exist on Order. Did you maybe mean getBillingAddressField()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
3267
                if ($billingAddress->RegionID) {
3268
                    $regionIDs['Billing'] = $billingAddress->RegionID;
3269
                }
3270
            }
3271
        }
3272
        if ($this->CanHaveShippingAddress()) {
3273
            if ($this->ShippingAddressID) {
0 ignored issues
show
Documentation introduced by
The property ShippingAddressID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3274
                if ($shippingAddress = $this->ShippingAddress()) {
0 ignored issues
show
Bug introduced by
The method ShippingAddress() does not exist on Order. Did you maybe mean getShippingAddressField()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
3275
                    if ($shippingAddress->ShippingRegionID) {
3276
                        $regionIDs['Shipping'] = $shippingAddress->ShippingRegionID;
3277
                    }
3278
                }
3279
            }
3280
        }
3281
        if (count($regionIDs)) {
3282
            //note the double-check with $this->CanHaveShippingAddress() and get_use_....
3283
            if ($this->CanHaveShippingAddress() && EcommerceConfig::get('OrderAddress', 'use_shipping_address_for_main_region_and_country') && $regionIDs['Shipping']) {
3284
                return EcommerceRegion::get()->byID($regionIDs['Shipping']);
3285
            } else {
3286
                return EcommerceRegion::get()->byID($regionIDs['Billing']);
3287
            }
3288
        } else {
3289
            return EcommerceRegion::get()->byID(EcommerceRegion::get_region_from_ip());
3290
        }
3291
    }
3292
3293
    /**
3294
     * Casted variable
3295
     * Currency is not the same as the standard one?
3296
     *
3297
     * @return bool
3298
     **/
3299
    public function HasAlternativeCurrency()
3300
    {
3301
        return $this->getHasAlternativeCurrency();
3302
    }
3303
    public function getHasAlternativeCurrency()
3304
    {
3305
        if ($currency = $this->CurrencyUsed()) {
0 ignored issues
show
Documentation Bug introduced by
The method CurrencyUsed does not exist on object<Order>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
3306
            if ($currency->IsDefault()) {
0 ignored issues
show
Coding Style introduced by
The if-else statement can be simplified to return !$currency->IsDefault();.
Loading history...
3307
                return false;
3308
            } else {
3309
                return true;
3310
            }
3311
        } else {
3312
            return false;
3313
        }
3314
    }
3315
3316
    /**
3317
     * Makes sure exchange rate is updated and maintained before order is submitted
3318
     * This method is public because it could be called from a shopping Cart Object.
3319
     **/
3320
    public function EnsureCorrectExchangeRate()
3321
    {
3322
        if (!$this->IsSubmitted()) {
3323
            $oldExchangeRate = $this->ExchangeRate;
0 ignored issues
show
Documentation introduced by
The property ExchangeRate does not exist on object<Order>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3324
            if ($currency = $this->CurrencyUsed()) {
0 ignored issues
show
Documentation Bug introduced by
The method CurrencyUsed does not exist on object<Order>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
3325
                if ($currency->IsDefault()) {
3326
                    $this->ExchangeRate = 0;
0 ignored issues
show
Documentation introduced by
The property ExchangeRate does not exist on object<Order>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3327
                } else {
3328
                    $this->ExchangeRate = $currency->getExchangeRate();
0 ignored issues
show
Documentation introduced by
The property ExchangeRate does not exist on object<Order>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3329
                }
3330
            } else {
3331
                $this->ExchangeRate = 0;
0 ignored issues
show
Documentation introduced by
The property ExchangeRate does not exist on object<Order>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3332
            }
3333
            if ($this->ExchangeRate != $oldExchangeRate) {
0 ignored issues
show
Documentation introduced by
The property ExchangeRate does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3334
                $this->write();
3335
            }
3336
        }
3337
    }
3338
3339
    /**
3340
     * speeds up processing by storing the IsSubmitted value
3341
     * we start with -1 to know if it has been requested before.
3342
     *
3343
     * @var bool
3344
     */
3345
    protected $_isSubmittedTempVar = -1;
3346
3347
    /**
3348
     * Casted variable - has the order been submitted?
3349
     * alias
3350
     * @param bool $recalculate
3351
     *
3352
     * @return bool
3353
     **/
3354
    public function IsSubmitted($recalculate = true)
3355
    {
3356
        return $this->getIsSubmitted($recalculate);
3357
    }
3358
3359
    /**
3360
     * Casted variable - has the order been submitted?
3361
     *
3362
     * @param bool $recalculate
3363
     *
3364
     * @return bool
3365
     **/
3366
    public function getIsSubmitted($recalculate = false)
3367
    {
3368
        if ($this->_isSubmittedTempVar === -1 || $recalculate) {
3369
            if ($this->SubmissionLog()) {
3370
                $this->_isSubmittedTempVar = true;
3371
            } else {
3372
                $this->_isSubmittedTempVar = false;
3373
            }
3374
        }
3375
3376
        return $this->_isSubmittedTempVar;
3377
    }
3378
3379
    /**
3380
     *
3381
     *
3382
     * @return bool
3383
     */
3384
    public function IsArchived()
3385
    {
3386
        $lastStep = OrderStep::last_order_step();
3387
        if ($lastStep) {
3388
            if ($lastStep->ID == $this->StatusID) {
0 ignored issues
show
Documentation introduced by
The property StatusID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3389
                return true;
3390
            }
3391
        }
3392
        return false;
3393
    }
3394
3395
    /**
3396
     * Submission Log for this Order (if any).
3397
     *
3398
     * @return Submission Log (OrderStatusLog_Submitted) | Null
3399
     **/
3400
    public function SubmissionLog()
3401
    {
3402
        $className = EcommerceConfig::get('OrderStatusLog', 'order_status_log_class_used_for_submitting_order');
3403
3404
        return $className::get()
3405
            ->Filter(array('OrderID' => $this->ID))
3406
            ->Last();
3407
    }
3408
3409
    /**
3410
     * Submission Log for this Order (if any).
3411
     *
3412
     * @return DateTime
0 ignored issues
show
Documentation introduced by
Should the return type not be DBField?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
3413
     **/
3414
    public function OrderDate()
3415
    {
3416
        $object = $this->SubmissionLog();
3417
        if ($object) {
3418
            $created = $object->Created;
3419
        } else {
3420
            $created = $this->LastEdited;
3421
        }
3422
3423
        return DBField::create_field('SS_Datetime', $created);
3424
    }
3425
3426
    /**
3427
     * @return int
3428
     */
3429
    public function SecondsSinceBeingSubmitted()
3430
    {
3431
        if ($submissionLog = $this->SubmissionLog()) {
3432
            return time() - strtotime($submissionLog->Created);
3433
        } else {
3434
            return 0;
3435
        }
3436
    }
3437
3438
    /**
3439
     * if the order can not be submitted,
3440
     * then the reasons why it can not be submitted
3441
     * will be returned by this method.
3442
     *
3443
     * @see Order::canSubmit
3444
     *
3445
     * @return ArrayList | null
0 ignored issues
show
Documentation introduced by
Should the return type not be ArrayList|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
3446
     */
3447
    public function SubmitErrors()
3448
    {
3449
        $al = null;
3450
        $extendedSubmitErrors = $this->extend('updateSubmitErrors');
3451
        if ($extendedSubmitErrors !== null && is_array($extendedSubmitErrors) && count($extendedSubmitErrors)) {
3452
            $al = ArrayList::create();
3453
            foreach ($extendedSubmitErrors as $returnResultArray) {
3454
                foreach ($returnResultArray as $issue) {
3455
                    if ($issue) {
3456
                        $al->push(ArrayData::create(array("Title" => $issue)));
3457
                    }
3458
                }
3459
            }
3460
        }
3461
        return $al;
3462
    }
3463
3464
    /**
3465
     * Casted variable - has the order been submitted?
3466
     *
3467
     * @param bool $withDetail
3468
     *
3469
     * @return string
3470
     **/
3471
    public function CustomerStatus($withDetail = true)
3472
    {
3473
        return $this->getCustomerStatus($withDetail);
3474
    }
3475
    public function getCustomerStatus($withDetail = true)
3476
    {
3477
        $str = '';
3478
        if ($this->MyStep()->ShowAsUncompletedOrder) {
3479
            $str = _t('Order.UNCOMPLETED', 'Uncompleted');
3480
        } elseif ($this->MyStep()->ShowAsInProcessOrder) {
3481
            $str = _t('Order.IN_PROCESS', 'In Process');
3482
        } elseif ($this->MyStep()->ShowAsCompletedOrder) {
3483
            $str = _t('Order.COMPLETED', 'Completed');
3484
        }
3485
        if ($withDetail) {
3486
            if (!$this->HideStepFromCustomer) {
0 ignored issues
show
Documentation introduced by
The property HideStepFromCustomer does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3487
                $str .= ' ('.$this->MyStep()->Name.')';
3488
            }
3489
        }
3490
3491
        return $str;
3492
    }
3493
3494
    /**
3495
     * Casted variable - does the order have a potential shipping address?
3496
     *
3497
     * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be array|integer|double|string|boolean?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
3498
     **/
3499
    public function CanHaveShippingAddress()
3500
    {
3501
        return $this->getCanHaveShippingAddress();
3502
    }
3503
    public function getCanHaveShippingAddress()
3504
    {
3505
        return EcommerceConfig::get('OrderAddress', 'use_separate_shipping_address');
3506
    }
3507
3508
    /**
3509
     * returns the link to view the Order
3510
     * WHY NOT CHECKOUT PAGE: first we check for cart page.
3511
     *
3512
     * @return CartPage | Null
3513
     */
3514
    public function DisplayPage()
3515
    {
3516
        if ($this->MyStep() && $this->MyStep()->AlternativeDisplayPage()) {
3517
            $page = $this->MyStep()->AlternativeDisplayPage();
3518
        } elseif ($this->IsSubmitted()) {
3519
            $page = DataObject::get_one('OrderConfirmationPage');
3520
        } else {
3521
            $page = DataObject::get_one(
3522
                'CartPage',
3523
                array('ClassName' => 'CartPage')
3524
            );
3525
            if (!$page) {
3526
                $page = DataObject::get_one('CheckoutPage');
3527
            }
3528
        }
3529
3530
        return $page;
3531
    }
3532
3533
    /**
3534
     * returns the link to view the Order
3535
     * WHY NOT CHECKOUT PAGE: first we check for cart page.
3536
     * If a cart page has been created then we refer through to Cart Page.
3537
     * Otherwise it will default to the checkout page.
3538
     *
3539
     * @param string $action - any action that should be added to the link.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $action not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
3540
     *
3541
     * @return String(URLSegment)
0 ignored issues
show
Documentation introduced by
The doc-type String(URLSegment) could not be parsed: Expected "|" or "end of type", but got "(" at position 6. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
3542
     */
3543
    public function Link($action = null)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
3544
    {
3545
        $page = $this->DisplayPage();
3546
        if ($page) {
3547
            return $page->getOrderLink($this->ID, $action);
3548
        } else {
3549
            user_error('A Cart / Checkout Page + an Order Confirmation Page needs to be setup for the e-commerce module to work.', E_USER_NOTICE);
3550
            $page = DataObject::get_one(
3551
                'ErrorPage',
3552
                array('ErrorCode' => '404')
3553
            );
3554
            if ($page) {
3555
                return $page->Link();
3556
            }
3557
        }
3558
    }
3559
3560
    /**
3561
     * Returns to link to access the Order's API.
3562
     *
3563
     * @param string $version
3564
     * @param string $extension
3565
     *
3566
     * @return String(URL)
0 ignored issues
show
Documentation introduced by
The doc-type String(URL) could not be parsed: Expected "|" or "end of type", but got "(" at position 6. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
3567
     */
3568
    public function APILink($version = 'v1', $extension = 'xml')
3569
    {
3570
        return Director::AbsoluteURL("/api/ecommerce/$version/Order/".$this->ID."/.$extension");
3571
    }
3572
3573
    /**
3574
     * returns the link to finalise the Order.
3575
     *
3576
     * @return String(URLSegment)
0 ignored issues
show
Documentation introduced by
The doc-type String(URLSegment) could not be parsed: Expected "|" or "end of type", but got "(" at position 6. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
3577
     */
3578
    public function CheckoutLink()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
3579
    {
3580
        $page = DataObject::get_one('CheckoutPage');
3581
        if ($page) {
3582
            return $page->Link();
3583
        } else {
3584
            $page = DataObject::get_one(
3585
                'ErrorPage',
3586
                array('ErrorCode' => '404')
3587
            );
3588
            if ($page) {
3589
                return $page->Link();
3590
            }
3591
        }
3592
    }
3593
3594
    /**
3595
     * Converts the Order into HTML, based on the Order Template.
3596
     *
3597
     * @return HTML Object
0 ignored issues
show
Documentation introduced by
Should the return type not be DBField?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
3598
     **/
3599
    public function ConvertToHTML()
3600
    {
3601
        Config::nest();
3602
        Config::inst()->update('SSViewer', 'theme_enabled', true);
3603
        $html = $this->renderWith('Order');
3604
        Config::unnest();
3605
        $html = preg_replace('/(\s)+/', ' ', $html);
3606
3607
        return DBField::create_field('HTMLText', $html);
3608
    }
3609
3610
    /**
3611
     * Converts the Order into a serialized string
3612
     * TO DO: check if this works and check if we need to use special sapphire serialization code.
3613
     *
3614
     * @return string - serialized object
3615
     **/
3616
    public function ConvertToString()
3617
    {
3618
        return serialize($this->addHasOneAndHasManyAsVariables());
3619
    }
3620
3621
    /**
3622
     * Converts the Order into a JSON object
3623
     * TO DO: check if this works and check if we need to use special sapphire JSON code.
3624
     *
3625
     * @return string -  JSON
3626
     **/
3627
    public function ConvertToJSON()
3628
    {
3629
        return json_encode($this->addHasOneAndHasManyAsVariables());
3630
    }
3631
3632
    /**
3633
     * returns itself wtih more data added as variables.
3634
     * We add has_one and has_many as variables like this: $this->MyHasOne_serialized = serialize($this->MyHasOne()).
3635
     *
3636
     * @return Order - with most important has one and has many items included as variables.
3637
     **/
3638
    protected function addHasOneAndHasManyAsVariables()
3639
    {
3640
        $object = clone $this;
3641
        $object->Member_serialized = serialize($this->Member());
0 ignored issues
show
Documentation introduced by
The property Member_serialized does not exist on object<Order>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Bug introduced by
The method Member() does not exist on Order. Did you maybe mean CreateOrReturnExistingMember()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
3642
        $object->BillingAddress_serialized = serialize($this->BillingAddress());
0 ignored issues
show
Documentation introduced by
The property BillingAddress_serialized does not exist on object<Order>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Bug introduced by
The method BillingAddress() does not exist on Order. Did you maybe mean getBillingAddressField()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
3643
        $object->ShippingAddress_serialized = serialize($this->ShippingAddress());
0 ignored issues
show
Documentation introduced by
The property ShippingAddress_serialized does not exist on object<Order>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Bug introduced by
The method ShippingAddress() does not exist on Order. Did you maybe mean getShippingAddressField()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
3644
        $object->Attributes_serialized = serialize($this->Attributes());
0 ignored issues
show
Documentation introduced by
The property Attributes_serialized does not exist on object<Order>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Bug introduced by
The method Attributes() does not exist on Order. Did you maybe mean getOrderAttributesByType()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
3645
        $object->OrderStatusLogs_serialized = serialize($this->OrderStatusLogs());
0 ignored issues
show
Documentation introduced by
The property OrderStatusLogs_serialized does not exist on object<Order>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Bug introduced by
The method OrderStatusLogs() does not exist on Order. Did you maybe mean getOrderStatusLogsTableField()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
3646
        $object->Payments_serialized = serialize($this->Payments());
0 ignored issues
show
Documentation introduced by
The property Payments_serialized does not exist on object<Order>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Bug introduced by
The method Payments() does not exist on Order. Did you maybe mean getPaymentsField()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
3647
        $object->Emails_serialized = serialize($this->Emails());
0 ignored issues
show
Documentation introduced by
The property Emails_serialized does not exist on object<Order>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Bug introduced by
The method Emails() does not exist on Order. Did you maybe mean getEmailsTableField()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
3648
3649
        return $object;
3650
    }
3651
3652
    /*******************************************************
3653
       * 9. TEMPLATE RELATED STUFF
3654
    *******************************************************/
3655
3656
    /**
3657
     * returns the instance of EcommerceConfigAjax for use in templates.
3658
     * In templates, it is used like this:
3659
     * $EcommerceConfigAjax.TableID.
3660
     *
3661
     * @return EcommerceConfigAjax
0 ignored issues
show
Documentation introduced by
Should the return type not be EcommerceConfigAjaxDefinitions?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
3662
     **/
3663
    public function AJAXDefinitions()
3664
    {
3665
        return EcommerceConfigAjax::get_one($this);
3666
    }
3667
3668
    /**
3669
     * returns the instance of EcommerceDBConfig.
3670
     *
3671
     * @return EcommerceDBConfig
3672
     **/
3673
    public function EcomConfig()
3674
    {
3675
        return EcommerceDBConfig::current_ecommerce_db_config();
3676
    }
3677
3678
    /**
3679
     * Collects the JSON data for an ajax return of the cart.
3680
     *
3681
     * @param array $js
3682
     *
3683
     * @return array (for use in AJAX for JSON)
3684
     **/
3685
    public function updateForAjax(array $js)
3686
    {
3687
        $function = EcommerceConfig::get('Order', 'ajax_subtotal_format');
3688
        if (is_array($function)) {
3689
            list($function, $format) = $function;
3690
        }
3691
        $subTotal = $this->$function();
3692
        if (isset($format)) {
3693
            $subTotal = $subTotal->$format();
3694
            unset($format);
3695
        }
3696
        $function = EcommerceConfig::get('Order', 'ajax_total_format');
3697
        if (is_array($function)) {
3698
            list($function, $format) = $function;
3699
        }
3700
        $total = $this->$function();
3701
        if (isset($format)) {
3702
            $total = $total->$format();
3703
        }
3704
        $ajaxObject = $this->AJAXDefinitions();
3705
        $js[] = array(
3706
            't' => 'id',
3707
            's' => $ajaxObject->TableSubTotalID(),
3708
            'p' => 'innerHTML',
3709
            'v' => $subTotal,
3710
        );
3711
        $js[] = array(
3712
            't' => 'id',
3713
            's' => $ajaxObject->TableTotalID(),
3714
            'p' => 'innerHTML',
3715
            'v' => $total,
3716
        );
3717
        $js[] = array(
3718
            't' => 'class',
3719
            's' => $ajaxObject->TotalItemsClassName(),
3720
            'p' => 'innerHTML',
3721
            'v' => $this->TotalItems($recalculate = true),
3722
        );
3723
        $js[] = array(
3724
            't' => 'class',
3725
            's' => $ajaxObject->TotalItemsTimesQuantityClassName(),
3726
            'p' => 'innerHTML',
3727
            'v' => $this->TotalItemsTimesQuantity(),
3728
        );
3729
        $js[] = array(
3730
            't' => 'class',
3731
            's' => $ajaxObject->ExpectedCountryClassName(),
3732
            'p' => 'innerHTML',
3733
            'v' => $this->ExpectedCountryName(),
3734
        );
3735
3736
        return $js;
3737
    }
3738
3739
    /**
3740
     * @ToDO: move to more appropriate class
3741
     *
3742
     * @return float
3743
     **/
3744
    public function SubTotalCartValue()
3745
    {
3746
        return $this->SubTotal;
0 ignored issues
show
Documentation introduced by
The property SubTotal does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3747
    }
3748
3749
    /*******************************************************
3750
       * 10. STANDARD SS METHODS (requireDefaultRecords, onBeforeDelete, etc...)
3751
    *******************************************************/
3752
3753
    /**
3754
     *standard SS method.
3755
     **/
3756
    public function populateDefaults()
3757
    {
3758
        parent::populateDefaults();
3759
    }
3760
3761
    public function onBeforeWrite()
3762
    {
3763
        parent::onBeforeWrite();
3764
        if (! $this->getCanHaveShippingAddress()) {
3765
            $this->UseShippingAddress = false;
0 ignored issues
show
Documentation introduced by
The property UseShippingAddress does not exist on object<Order>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3766
        }
3767
        if (!$this->CurrencyUsedID) {
0 ignored issues
show
Documentation introduced by
The property CurrencyUsedID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3768
            $this->CurrencyUsedID = EcommerceCurrency::default_currency_id();
0 ignored issues
show
Documentation introduced by
The property CurrencyUsedID does not exist on object<Order>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3769
        }
3770
        if (!$this->SessionID) {
0 ignored issues
show
Documentation introduced by
The property SessionID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3771
            $generator = Injector::inst()->create('RandomGenerator');
3772
            $token = $generator->randomToken('sha1');
3773
            $this->SessionID = substr($token, 0, 32);
0 ignored issues
show
Documentation introduced by
The property SessionID does not exist on object<Order>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3774
        }
3775
    }
3776
3777
    /**
3778
     * standard SS method
3779
     * adds the ability to update order after writing it.
3780
     **/
3781
    public function onAfterWrite()
0 ignored issues
show
Coding Style introduced by
onAfterWrite uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
3782
    {
3783
        parent::onAfterWrite();
3784
        //crucial!
3785
        self::set_needs_recalculating(true, $this->ID);
3786
        // quick double-check
3787
        if ($this->IsCancelled() && ! $this->IsArchived()) {
3788
            $this->Archive($avoidWrites = true);
3789
        }
3790
        if ($this->IsSubmitted($recalculate = true)) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
3791
            //do nothing
3792
        } else {
3793
            if ($this->StatusID) {
0 ignored issues
show
Documentation introduced by
The property StatusID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
3794
                $this->calculateOrderAttributes($recalculate = false);
3795
                if (EcommerceRole::current_member_is_shop_admin()) {
3796
                    if (isset($_REQUEST['SubmitOrderViaCMS'])) {
3797
                        $this->tryToFinaliseOrder();
3798
                        //just in case it writes again...
3799
                        unset($_REQUEST['SubmitOrderViaCMS']);
3800
                    }
3801
                }
3802
            }
3803
        }
3804
    }
3805
3806
    /**
3807
     *standard SS method.
3808
     *
3809
     * delete attributes, statuslogs, and payments
3810
     * THIS SHOULD NOT BE USED AS ORDERS SHOULD BE CANCELLED NOT DELETED
3811
     */
3812
    public function onBeforeDelete()
3813
    {
3814
        parent::onBeforeDelete();
3815
        if ($attributes = $this->Attributes()) {
0 ignored issues
show
Bug introduced by
The method Attributes() does not exist on Order. Did you maybe mean getOrderAttributesByType()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
3816
            foreach ($attributes as $attribute) {
3817
                $attribute->delete();
3818
                $attribute->destroy();
3819
            }
3820
        }
3821
3822
        //THE REST WAS GIVING ERRORS - POSSIBLY DUE TO THE FUNNY RELATIONSHIP (one-one, two times...)
3823
        /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
66% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
3824
        if($billingAddress = $this->BillingAddress()) {
3825
            if($billingAddress->exists()) {
3826
                $billingAddress->delete();
3827
                $billingAddress->destroy();
3828
            }
3829
        }
3830
        if($shippingAddress = $this->ShippingAddress()) {
3831
            if($shippingAddress->exists()) {
3832
                $shippingAddress->delete();
3833
                $shippingAddress->destroy();
3834
            }
3835
        }
3836
3837
        if($statuslogs = $this->OrderStatusLogs()){
3838
            foreach($statuslogs as $log){
3839
                $log->delete();
3840
                $log->destroy();
3841
            }
3842
        }
3843
        if($payments = $this->Payments()){
3844
            foreach($payments as $payment){
3845
                $payment->delete();
3846
                $payment->destroy();
3847
            }
3848
        }
3849
        if($emails = $this->Emails()) {
3850
            foreach($emails as $email){
3851
                $email->delete();
3852
                $email->destroy();
3853
            }
3854
        }
3855
        */
3856
    }
3857
3858
    /*******************************************************
3859
       * 11. DEBUG
3860
    *******************************************************/
3861
3862
    /**
3863
     * Debug helper method.
3864
     * Can be called from /shoppingcart/debug/.
3865
     *
3866
     * @return string
3867
     */
3868
    public function debug()
3869
    {
3870
        $this->calculateOrderAttributes(true);
3871
3872
        return EcommerceTaskDebugCart::debug_object($this);
3873
    }
3874
}
3875