Completed
Push — master ( 377c29...c08981 )
by Nicolaas
03:37
created

Order::IsCustomerCancelled()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
eloc 4
nc 2
nop 0
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
    );
117
118
    /**
119
     * standard SS variable.
120
     *
121
     * @var string
122
     */
123
    private static $default_sort = '"LastEdited" DESC';
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...
124
125
    /**
126
     * standard SS variable.
127
     *
128
     * @var array
129
     */
130
    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...
131
        'OrderEmail' => 'Varchar',
132
        'EmailLink' => 'Varchar',
133
        'PrintLink' => 'Varchar',
134
        'ShareLink' => 'Varchar',
135
        'FeedbackLink' => 'Varchar',
136
        'RetrieveLink' => 'Varchar',
137
        'Title' => 'Varchar',
138
        'Total' => 'Currency',
139
        'TotalAsMoney' => 'Money',
140
        'SubTotal' => 'Currency',
141
        'SubTotalAsMoney' => 'Money',
142
        'TotalPaid' => 'Currency',
143
        'TotalPaidAsMoney' => 'Money',
144
        'TotalOutstanding' => 'Currency',
145
        'TotalOutstandingAsMoney' => 'Money',
146
        'HasAlternativeCurrency' => 'Boolean',
147
        'TotalItems' => 'Double',
148
        'TotalItemsTimesQuantity' => 'Double',
149
        'IsCancelled' => 'Boolean',
150
        'IsPaidNice' => 'Boolean',
151
        'Country' => 'Varchar(3)', //This is the applicable country for the order - for tax purposes, etc....
152
        'FullNameCountry' => 'Varchar',
153
        'IsSubmitted' => 'Boolean',
154
        'CustomerStatus' => 'Varchar',
155
        'CanHaveShippingAddress' => 'Boolean',
156
    );
157
158
    /**
159
     * standard SS variable.
160
     *
161
     * @var string
162
     */
163
    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...
164
    public function i18n_singular_name()
165
    {
166
        return _t('Order.ORDER', 'Order');
167
    }
168
169
    /**
170
     * standard SS variable.
171
     *
172
     * @var string
173
     */
174
    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...
175
    public function i18n_plural_name()
176
    {
177
        return _t('Order.ORDERS', 'Orders');
178
    }
179
180
    /**
181
     * Standard SS variable.
182
     *
183
     * @var string
184
     */
185
    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...
186
187
    /**
188
     * Tells us if an order needs to be recalculated
189
     * can save one for each order...
190
     *
191
     * @var array
192
     */
193
    private static $_needs_recalculating = array();
194
195
    /**
196
     * @param bool (optional) $b
197
     * @param int (optional)  $orderID
198
     *
199
     * @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...
200
     */
201
    public static function set_needs_recalculating($b = true, $orderID = 0)
202
    {
203
        self::$_needs_recalculating[$orderID] = $b;
204
    }
205
206
    /**
207
     * @param int (optional) $orderID
208
     *
209
     * @return bool
210
     */
211
    public static function get_needs_recalculating($orderID = 0)
212
    {
213
        return isset(self::$_needs_recalculating[$orderID]) ? self::$_needs_recalculating[$orderID] : false;
214
    }
215
216
    /**
217
     * Total Items : total items in cart
218
     * We start with -1 to easily identify if it has been run before.
219
     *
220
     * @var int
221
     */
222
    protected $totalItems = null;
223
224
    /**
225
     * Total Items : total items in cart
226
     * We start with -1 to easily identify if it has been run before.
227
     *
228
     * @var float
229
     */
230
    protected $totalItemsTimesQuantity = null;
231
232
    /**
233
     * Returns a set of modifier forms for use in the checkout order form,
234
     * Controller is optional, because the orderForm has its own default controller.
235
     *
236
     * This method only returns the Forms that should be included outside
237
     * the editable table... Forms within it can be called
238
     * from through the modifier itself.
239
     *
240
     * @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...
241
     * @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...
242
     *
243
     * @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...
244
     **/
245
    public function getModifierForms(Controller $optionalController = null, Validator $optionalValidator = null)
246
    {
247
        $arrayList = new ArrayList();
248
        $modifiers = $this->Modifiers();
249
        if ($modifiers->count()) {
250
            foreach ($modifiers as $modifier) {
251
                if ($modifier->ShowForm()) {
252
                    if ($form = $modifier->getModifierForm($optionalController, $optionalValidator)) {
253
                        $form->ShowFormInEditableOrderTable = $modifier->ShowFormInEditableOrderTable();
254
                        $form->ShowFormOutsideEditableOrderTable = $modifier->ShowFormOutsideEditableOrderTable();
255
                        $form->ModifierName = $modifier->ClassName;
256
                        $arrayList->push($form);
257
                    }
258
                }
259
            }
260
        }
261
        if ($arrayList->count()) {
262
            return $arrayList;
263
        } else {
264
            return;
265
        }
266
    }
267
268
    /**
269
     * This function returns the OrderSteps.
270
     *
271
     * @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...
272
     **/
273
    public static function get_order_status_options()
274
    {
275
        return OrderStep::get();
276
    }
277
278
    /**
279
     * Like the standard byID, but it checks whether we are allowed to view the order.
280
     *
281
     * @return: Order | Null
282
     **/
283
    public static function get_by_id_if_can_view($id)
284
    {
285
        $order = Order::get()->byID($id);
286
        if ($order && $order->canView()) {
287
            if ($order->IsSubmitted()) {
288
                // LITTLE HACK TO MAKE SURE WE SHOW THE LATEST INFORMATION!
289
                $order->tryToFinaliseOrder();
290
            }
291
292
            return $order;
293
        }
294
295
        return;
296
    }
297
298
    /**
299
     * returns a Datalist with the submitted order log included
300
     * this allows you to sort the orders by their submit dates.
301
     * You can retrieve this list and then add more to it (e.g. additional filters, additional joins, etc...).
302
     *
303
     * @param bool $onlySubmittedOrders - only include Orders that have already been submitted.
304
     * @param bool $includeCancelledOrders - only include Orders that have already been submitted.
305
     *
306
     * @return DataList (Orders)
307
     */
308
    public static function get_datalist_of_orders_with_submit_record($onlySubmittedOrders = true, $includeCancelledOrders = false)
309
    {
310
        if ($onlySubmittedOrders) {
311
            $submittedOrderStatusLogClassName = EcommerceConfig::get('OrderStatusLog', 'order_status_log_class_used_for_submitting_order');
312
            $list = Order::get()
313
                ->LeftJoin('OrderStatusLog', '"Order"."ID" = "OrderStatusLog"."OrderID"')
314
                ->LeftJoin($submittedOrderStatusLogClassName, '"OrderStatusLog"."ID" = "'.$submittedOrderStatusLogClassName.'"."ID"')
315
                ->Sort('OrderStatusLog.Created', 'ASC');
316
            $where = ' ("OrderStatusLog"."ClassName" = \''.$submittedOrderStatusLogClassName.'\') ';
317
        } else {
318
            $list = Order::get();
319
            $where = ' ("StatusID" > 0) ';
320
        }
321
        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...
322
            //do nothing...
323
        } else {
324
            $where .= ' AND ("CancelledByID" = 0 OR "CancelledByID" IS NULL)';
325
        }
326
        $list = $list->where($where);
327
328
        return $list;
329
    }
330
331
/*******************************************************
332
   * 1. CMS STUFF
333
*******************************************************/
334
335
    /**
336
     * fields that we remove from the parent::getCMSFields object set.
337
     *
338
     * @var array
339
     */
340
    protected $fieldsAndTabsToBeRemoved = array(
341
        'MemberID',
342
        'Attributes',
343
        'SessionID',
344
        'Emails',
345
        'BillingAddressID',
346
        'ShippingAddressID',
347
        'UseShippingAddress',
348
        'OrderStatusLogs',
349
        'Payments',
350
        'OrderDate',
351
        'ExchangeRate',
352
        'CurrencyUsedID',
353
        'StatusID',
354
        'Currency',
355
    );
356
357
    /**
358
     * STANDARD SILVERSTRIPE STUFF.
359
     **/
360
    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...
361
        'Title' => 'Title',
362
        'Status.Title' => 'Next Step',
363
        'Member.Surname' => 'Name',
364
        'Member.Email' => 'Email',
365
        'TotalAsMoney.Nice' => 'Total',
366
        'TotalItemsTimesQuantity' => 'Units',
367
        'IsPaidNice' => 'Paid'
368
    );
369
370
    /**
371
     * STANDARD SILVERSTRIPE STUFF.
372
     *
373
     * @todo: how to translate this?
374
     **/
375
    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...
376
        'ID' => array(
377
            'field' => 'NumericField',
378
            'title' => 'Order Number',
379
        ),
380
        'MemberID' => array(
381
            'field' => 'TextField',
382
            'filter' => 'OrderFilters_MemberAndAddress',
383
            'title' => 'Customer Details',
384
        ),
385
        'Created' => array(
386
            'field' => 'TextField',
387
            'filter' => 'OrderFilters_AroundDateFilter',
388
            'title' => 'Date (e.g. Today, 1 jan 2007, or last week)',
389
        ),
390
        //make sure to keep the items below, otherwise they do not show in form
391
        'StatusID' => array(
392
            'filter' => 'OrderFilters_MultiOptionsetStatusIDFilter',
393
        ),
394
        'CancelledByID' => array(
395
            'filter' => 'OrderFilters_HasBeenCancelled',
396
            'title' => 'Cancelled by ...',
397
        ),
398
    );
399
400
    /**
401
     * Determine which properties on the DataObject are
402
     * searchable, and map them to their default {@link FormField}
403
     * representations. Used for scaffolding a searchform for {@link ModelAdmin}.
404
     *
405
     * Some additional logic is included for switching field labels, based on
406
     * how generic or specific the field type is.
407
     *
408
     * Used by {@link SearchContext}.
409
     *
410
     * @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...
411
     *                       'fieldClasses': Associative array of field names as keys and FormField classes as values
412
     *                       'restrictFields': Numeric array of a field name whitelist
413
     *
414
     * @return FieldList
415
     */
416
    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...
417
    {
418
        $fieldList = parent::scaffoldSearchFields($_params);
419
420
        //for sales to action only show relevant ones ...
421
        if(Controller::curr() && Controller::curr()->class === 'SalesAdmin') {
422
            $statusOptions = OrderStep::admin_manageable_steps();
423
        } else {
424
            $statusOptions = OrderStep::get();
425
        }
426
        if ($statusOptions && $statusOptions->count()) {
427
            $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...
428
            $preSelected = array();
429
            $createdOrderStatus = $statusOptions->First();
430
            if ($createdOrderStatus) {
431
                $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...
432
            }
433
            $arrayOfStatusOptions = clone $statusOptions->map('ID', 'Title');
434
            $arrayOfStatusOptionsFinal = array();
435
            if (count($arrayOfStatusOptions)) {
436
                foreach ($arrayOfStatusOptions as $key => $value) {
437
                    if (isset($_GET['q']['StatusID'][$key])) {
438
                        $preSelected[$key] = $key;
439
                    }
440
                    $count = Order::get()
441
                        ->Filter(array('StatusID' => intval($key)))
442
                        ->count();
443
                    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...
444
                        //do nothing
445
                    } else {
446
                        $arrayOfStatusOptionsFinal[$key] = $value." ($count)";
447
                    }
448
                }
449
            }
450
            $statusField = new CheckboxSetField(
451
                'StatusID',
452
                Injector::inst()->get('OrderStep')->i18n_singular_name(),
453
                $arrayOfStatusOptionsFinal,
454
                $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...
455
            );
456
            $fieldList->push($statusField);
457
        }
458
        $fieldList->push(new DropdownField('CancelledByID', 'Cancelled', array(-1 => '(Any)', 1 => 'yes', 0 => 'no')));
459
460
        //allow changes
461
        $this->extend('scaffoldSearchFields', $fieldList, $_params);
462
463
        return $fieldList;
464
    }
465
466
    /**
467
     * link to edit the record.
468
     *
469
     * @param string | Null $action - e.g. edit
470
     *
471
     * @return string
472
     */
473
    public function CMSEditLink($action = null)
474
    {
475
        return CMSEditLinkAPI::find_edit_link_for_object($this, $action, 'sales-advanced');
0 ignored issues
show
Unused Code introduced by
The call to CMSEditLinkAPI::find_edit_link_for_object() has too many arguments starting with 'sales-advanced'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
476
    }
477
478
    /**
479
     * STANDARD SILVERSTRIPE STUFF
480
     * broken up into submitted and not (yet) submitted.
481
     **/
482
    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...
483
    {
484
        $fields = $this->scaffoldFormFields(array(
485
            // Don't allow has_many/many_many relationship editing before the record is first saved
486
            'includeRelations' => false,
487
            'tabbed' => true,
488
            'ajaxSafe' => true
489
        ));
490
        $fields->insertBefore(
491
            Tab::create(
492
                'Next',
493
                _t('Order.NEXT_TAB', 'Action')
494
            ),
495
            '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...
496
        );
497
        $fields->addFieldsToTab(
498
            'Root',
499
            array(
500
                Tab::create(
501
                    "Items",
502
                    _t('Order.ITEMS_TAB', 'Items')
503
                ),
504
                Tab::create(
505
                    "Extras",
506
                    _t('Order.MODIFIERS_TAB', 'Adjustments')
507
                ),
508
                Tab::create(
509
                    'Emails',
510
                    _t('Order.EMAILS_TAB', 'Emails')
511
                ),
512
                Tab::create(
513
                    'Payments',
514
                    _t('Order.PAYMENTS_TAB', 'Payment')
515
                ),
516
                Tab::create(
517
                    'Account',
518
                    _t('Order.ACCOUNT_TAB', 'Account')
519
                ),
520
                Tab::create(
521
                    'Currency',
522
                    _t('Order.CURRENCY_TAB', 'Currency')
523
                ),
524
                Tab::create(
525
                    'Addresses',
526
                    _t('Order.ADDRESSES_TAB', 'Addresses')
527
                ),
528
                Tab::create(
529
                    'Log',
530
                    _t('Order.LOG_TAB', 'Notes')
531
                ),
532
                Tab::create(
533
                    'Cancellations',
534
                    _t('Order.CANCELLATION_TAB', 'Cancel')
535
                ),
536
            )
537
        );
538
        //as we are no longer using the parent:;getCMSFields
539
        // we had to add the updateCMSFields hook.
540
        $this->extend('updateCMSFields', $fields);
541
        $currentMember = Member::currentUser();
542
        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...
543
            $firstStep = OrderStep::get()->First();
544
            $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...
545
            $this->write();
546
        }
547
        $submitted = $this->IsSubmitted() ? true : false;
548
        if ($submitted) {
549
            //TODO
550
            //Having trouble here, as when you submit the form (for example, a payment confirmation)
551
            //as the step moves forward, meaning the fields generated are incorrect, causing an error
552
            //"I can't handle sub-URLs of a Form object." generated by the RequestHandler.
553
            //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
554
            //Or something similar.
555
            //why not check if the URL == $this->CMSEditLink()
556
            //and only tryToFinaliseOrder if this is true....
557
            if ($_SERVER['REQUEST_URI'] == $this->CMSEditLink() || $_SERVER['REQUEST_URI'] == $this->CMSEditLink('edit')) {
558
                $this->tryToFinaliseOrder();
559
            }
560
        } else {
561
            $this->init(true);
562
            $this->calculateOrderAttributes(true);
563
            Session::set('EcommerceOrderGETCMSHack', $this->ID);
564
        }
565
        if ($submitted) {
566
            $this->fieldsAndTabsToBeRemoved[] = 'CustomerOrderNote';
567
        } else {
568
            $this->fieldsAndTabsToBeRemoved[] = 'Emails';
569
        }
570
        foreach ($this->fieldsAndTabsToBeRemoved as $field) {
571
            $fields->removeByName($field);
572
        }
573
        $orderSummaryConfig = GridFieldConfig_Base::create();
574
        $orderSummaryConfig->removeComponentsByType('GridFieldToolbarHeader');
575
        // $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...
576
        $orderSummaryConfig->removeComponentsByType('GridFieldFilterHeader');
577
        $orderSummaryConfig->removeComponentsByType('GridFieldPageCount');
578
        $orderSummaryConfig->removeComponentsByType('GridFieldPaginator');
579
        $nextFieldArray = array(
580
            LiteralField::create('CssFix', '<style>#Root_Next h2.form-control {padding: 0!important; margin: 0!important; padding-top: 4em!important;}</style>'),
581
            HeaderField::create('MyOrderStepHeader', _t('Order.CURRENT_STATUS', '1. Current Status')),
582
            $this->OrderStepField(),
583
            GridField::create(
584
                'OrderSummary',
585
                _t('Order.CURRENT_STATUS', 'Summary'),
586
                ArrayList::create(array($this)),
587
                $orderSummaryConfig
588
            )
589
        );
590
        $keyNotes = OrderStatusLog::get()->filter(
591
            array(
592
                'OrderID' => $this->ID,
593
                'ClassName' => 'OrderStatusLog'
594
            )
595
        );
596
        if ($keyNotes->count()) {
597
            $notesSummaryConfig = GridFieldConfig_RecordViewer::create();
598
            $notesSummaryConfig->removeComponentsByType('GridFieldToolbarHeader');
599
            $notesSummaryConfig->removeComponentsByType('GridFieldFilterHeader');
600
            // $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...
601
            $notesSummaryConfig->removeComponentsByType('GridFieldPageCount');
602
            $notesSummaryConfig->removeComponentsByType('GridFieldPaginator');
603
            $nextFieldArray = array_merge(
604
                $nextFieldArray,
605
                array(
606
                    HeaderField::create('KeyNotesHeader', _t('Order.KEY_NOTES_HEADER', 'Key Notes')),
607
                    GridField::create(
608
                        'OrderStatusLogSummary',
609
                        _t('Order.CURRENT_KEY_NOTES', 'Key Notes'),
610
                        $keyNotes,
611
                        $notesSummaryConfig
612
                    )
613
                )
614
            );
615
        }
616
        $nextFieldArray = array_merge(
617
            $nextFieldArray,
618
            array(
619
                EcommerceCMSButtonField::create(
620
                    'AddNoteButton',
621
                    '/admin/sales/Order/EditForm/field/Order/item/' . $this->ID . '/ItemEditForm/field/OrderStatusLog/item/new',
622
                    _t('Order.ADD_NOTE', 'Add Note')
623
                )
624
            )
625
        );
626
        $nextFieldArray = array_merge(
627
            $nextFieldArray,
628
            array(
629
630
            )
631
        );
632
633
         //is the member is a shop admin they can always view it
634
635
        if (EcommerceRole::current_member_can_process_orders(Member::currentUser())) {
636
            $lastStep = OrderStep::get()->Last();
637
            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...
638
                $queueObjectSingleton = Injector::inst()->get('OrderProcessQueue');
639
                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...
640
                    $myQueueObjectField = GridField::create(
641
                        'MyQueueObjectField',
642
                        _t('Order.QUEUE_DETAILS', 'Queue Details'),
643
                        $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...
644
                        GridFieldConfig_RecordEditor::create()
645
                    );
646
                } else {
647
                    $myQueueObjectField = LiteralField::create('MyQueueObjectField', '<p>'._t('Order.NOT_QUEUED','This order is not queued for future processing.').'</p>');
648
                }
649
                $nextFieldArray = array_merge(
650
                    $nextFieldArray,
651
                    array(
652
                        HeaderField::create('OrderStepNextStepHeader', _t('Order.ACTION_NEXT_STEP', '2. Action Next Step')),
653
                        $myQueueObjectField,
654
                        HeaderField::create('ActionNextStepManually', _t('Order.MANUAL_STATUS_CHANGE', '3. Move Order Along')),
655
                        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>'),
656
                        EcommerceCMSButtonField::create(
657
                            'StatusIDExplanation',
658
                            $this->CMSEditLink(),
659
                            _t('Order.REFRESH', 'refresh now')
660
                        )
661
                    )
662
                );
663
            }
664
        }
665
        $fields->addFieldsToTab(
666
            'Root.Next',
667
            $nextFieldArray
668
        );
669
670
        $this->MyStep()->addOrderStepFields($fields, $this);
671
672
        if ($submitted) {
673
            $permaLinkLabel = _t('Order.PERMANENT_LINK', 'Customer Link');
674
            $html = '<p>'.$permaLinkLabel.': <a href="'.$this->getRetrieveLink().'">'.$this->getRetrieveLink().'</a></p>';
675
            $shareLinkLabel = _t('Order.SHARE_LINK', 'Share Link');
676
            $html .= '<p>'.$shareLinkLabel.': <a href="'.$this->getShareLink().'">'.$this->getShareLink().'</a></p>';
677
            $feedbackLinkLabel = _t('Order.FEEDBACK_LINK', 'Feedback Link');
678
            $html .= '<p>'.$feedbackLinkLabel.': <a href="'.$this->getFeedbackLink().'">'.$this->getFeedbackLink().'</a></p>';
679
            $js = "window.open(this.href, 'payment', 'toolbar=0,scrollbars=1,location=1,statusbar=1,menubar=0,resizable=1,width=800,height=600'); return false;";
680
            $link = $this->getPrintLink();
681
            $label = _t('Order.PRINT_INVOICE', 'invoice');
682
            $linkHTML = '<a href="'.$link.'" onclick="'.$js.'">'.$label.'</a>';
683
            $linkHTML .= ' | ';
684
            $link = $this->getPackingSlipLink();
685
            $label = _t('Order.PRINT_PACKING_SLIP', 'packing slip');
686
            $labelPrint = _t('Order.PRINT', 'Print');
687
            $linkHTML .= '<a href="'.$link.'" onclick="'.$js.'">'.$label.'</a>';
688
            $html .= '<h3>';
689
            $html .= $labelPrint.': '.$linkHTML;
690
            $html .= '</h3>';
691
692
            $fields->addFieldToTab(
693
                'Root.Main',
694
                LiteralField::create('getPrintLinkANDgetPackingSlipLink', $html)
695
            );
696
697
            //add order here as well.
698
            $fields->addFieldToTab(
699
                'Root.Main',
700
                new LiteralField(
701
                    'MainDetails',
702
                    '<iframe src="'.$this->getPrintLink().'" width="100%" height="2500" style="border: 5px solid #2e7ead; border-radius: 2px;"></iframe>')
703
            );
704
            $fields->addFieldsToTab(
705
                'Root.Items',
706
                array(
707
                    GridField::create(
708
                        'Items_Sold',
709
                        'Items Sold',
710
                        $this->Items(),
711
                        new GridFieldConfig_RecordViewer
712
                    )
713
                )
714
            );
715
            $fields->addFieldsToTab(
716
                'Root.Extras',
717
                array(
718
                    GridField::create(
719
                        'Modifications',
720
                        'Price (and other) adjustments',
721
                        $this->Modifiers(),
722
                        new GridFieldConfig_RecordViewer
723
                    )
724
                )
725
            );
726
            $fields->addFieldsToTab(
727
                'Root.Emails',
728
                array(
729
                    $this->getEmailsTableField()
730
                )
731
            );
732
            $fields->addFieldsToTab(
733
                'Root.Payments',
734
                array(
735
                    $this->getPaymentsField(),
736
                    new ReadOnlyField('TotalPaidNice', _t('Order.TOTALPAID', 'Total Paid'), $this->TotalPaidAsCurrencyObject()->Nice()),
737
                    new ReadOnlyField('TotalOutstandingNice', _t('Order.TOTALOUTSTANDING', 'Total Outstanding'), $this->getTotalOutstandingAsMoney()->Nice())
738
                )
739
            );
740
            if ($this->canPay()) {
741
                $link = EcommercePaymentController::make_payment_link($this->ID);
742
                $js = "window.open(this.href, 'payment', 'toolbar=0,scrollbars=1,location=1,statusbar=1,menubar=0,resizable=1,width=800,height=600'); return false;";
743
                $header = _t('Order.MAKEPAYMENT', 'make payment');
744
                $label = _t('Order.MAKEADDITIONALPAYMENTNOW', 'make additional payment now');
745
                $linkHTML = '<a href="'.$link.'" onclick="'.$js.'">'.$label.'</a>';
746
                $fields->addFieldToTab('Root.Payments', new HeaderField('MakeAdditionalPaymentHeader', $header, 3));
747
                $fields->addFieldToTab('Root.Payments', new LiteralField('MakeAdditionalPayment', $linkHTML));
748
            }
749
            //member
750
            $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...
751
            if ($member && $member->exists()) {
752
                $fields->addFieldToTab('Root.Account', new LiteralField('MemberDetails', $member->getEcommerceFieldsForCMS()));
753
            } else {
754
                $fields->addFieldToTab('Root.Account', new LiteralField('MemberDetails',
755
                    '<p>'._t('Order.NO_ACCOUNT', 'There is no --- account --- associated with this order').'</p>'
756
                ));
757
            }
758
            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...
759
                $fields->addFieldToTab(
760
                    'Root.Account',
761
                    GridField::create(
762
                        'OrderFeedback',
763
                        Injector::inst()->get('OrderFeedback')->singular_name(),
764
                        OrderFeedback::get()->filter(array('OrderID' => $this->ID)),
765
                        GridFieldConfig_RecordViewer::create()
766
                    )
767
                );
768
            }
769
            $cancelledField = $fields->dataFieldByName('CancelledByID');
770
            $fields->removeByName('CancelledByID');
771
            $shopAdminAndCurrentCustomerArray = EcommerceRole::list_of_admins(true);
772
            if ($member && $member->exists()) {
773
                $shopAdminAndCurrentCustomerArray[$member->ID] = $member->getName();
774
            }
775
            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...
776
                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...
777
                    $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...
778
                }
779
            }
780
            if ($this->canCancel()) {
781
                $fields->addFieldsToTab(
782
                    'Root.Cancellations',
783
                    array(
784
                        DropdownField::create(
785
                            'CancelledByID',
786
                            $cancelledField->Title(),
787
                            $shopAdminAndCurrentCustomerArray
788
                        )
789
                    )
790
                );
791
            } else {
792
                $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...
793
                $fields->addFieldsToTab(
794
                    'Root.Cancellations',
795
                    ReadonlyField::create(
796
                        'CancelledByDisplay',
797
                        $cancelledField->Title(),
798
                        $cancelledBy
799
800
                    )
801
                );
802
            }
803
            $fields->addFieldToTab('Root.Log', $this->getOrderStatusLogsTableField_Archived());
804
            $submissionLog = $this->SubmissionLog();
805
            if ($submissionLog) {
806
                $fields->addFieldToTab('Root.Log',
807
                    ReadonlyField::create(
808
                        'SequentialOrderNumber',
809
                        _t('Order.SEQUENTIALORDERNUMBER', 'Consecutive order number'),
810
                        $submissionLog->SequentialOrderNumber
811
                    )->setRightTitle('e.g. 1,2,3,4,5...')
812
                );
813
            }
814
        } else {
815
            $linkText = _t(
816
                'Order.LOAD_THIS_ORDER',
817
                'load this order'
818
            );
819
            $message = _t(
820
                'Order.NOSUBMITTEDYET',
821
                '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 .',
822
                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...
823
            );
824
            $fields->addFieldToTab('Root.Next', new LiteralField('MainDetails', '<p>'.$message.'</p>'));
825
            $fields->addFieldToTab('Root.Items', $this->getOrderItemsField());
826
            $fields->addFieldToTab('Root.Extras', $this->getModifierTableField());
827
828
            //MEMBER STUFF
829
            $specialOptionsArray = array();
830
            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...
831
                $specialOptionsArray[0] = _t('Order.SELECTCUSTOMER', '--- Remover Customer ---');
832
                $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...
833
            } elseif ($currentMember) {
834
                $specialOptionsArray[0] = _t('Order.SELECTCUSTOMER', '--- Select Customers ---');
835
                $currentMemberID = $currentMember->ID;
836
                $specialOptionsArray[$currentMemberID] = _t('Order.ASSIGNTHISORDERTOME', '- Assign this order to me: ').$currentMember->getTitle();
837
            }
838
            //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...
839
            $memberArray = $specialOptionsArray + EcommerceRole::list_of_customers(true);
840
            $fields->addFieldToTab('Root.Next', new DropdownField('MemberID', _t('Order.SELECTCUSTOMER', 'Select Customer'), $memberArray), 'CustomerOrderNote');
841
            $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...
842
        }
843
        $fields->addFieldToTab('Root.Addresses', new HeaderField('BillingAddressHeader', _t('Order.BILLINGADDRESS', 'Billing Address')));
844
845
        $fields->addFieldToTab('Root.Addresses', $this->getBillingAddressField());
846
847
        if (EcommerceConfig::get('OrderAddress', 'use_separate_shipping_address')) {
848
            $fields->addFieldToTab('Root.Addresses', new HeaderField('ShippingAddressHeader', _t('Order.SHIPPINGADDRESS', 'Shipping Address')));
849
            $fields->addFieldToTab('Root.Addresses', new CheckboxField('UseShippingAddress', _t('Order.USESEPERATEADDRESS', 'Use separate shipping address?')));
850
            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...
851
                $fields->addFieldToTab('Root.Addresses', $this->getShippingAddressField());
852
            }
853
        }
854
        $currencies = EcommerceCurrency::get_list();
855
        if ($currencies && $currencies->count()) {
856
            $currencies = $currencies->map()->toArray();
857
            $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...
858
            $fields->addFieldToTab('Root.Currency', $currencyField = new DropdownField('CurrencyUsedID', _t('Order.CurrencyUsed', 'Currency Used'), $currencies));
859
            if ($this->IsSubmitted()) {
860
                $fields->replaceField('CurrencyUsedID', $fields->dataFieldByName('CurrencyUsedID')->performReadonlyTransformation());
861
            }
862
        } else {
863
            $fields->addFieldToTab('Root.Currency', new LiteralField('CurrencyInfo', '<p>You can not change currencies, because no currencies have been created.</p>'));
864
            $fields->replaceField('CurrencyUsedID', $fields->dataFieldByName('CurrencyUsedID')->performReadonlyTransformation());
865
        }
866
        $fields->addFieldToTab('Root.Log', new ReadonlyField('Created', _t('Root.CREATED', 'Created')));
867
        $fields->addFieldToTab('Root.Log', new ReadonlyField('LastEdited', _t('Root.LASTEDITED', 'Last saved')));
868
        $this->extend('updateCMSFields', $fields);
869
870
        return $fields;
871
    }
872
873
    /**
874
     * Field to add and edit Order Items.
875
     *
876
     * @return GridField
877
     */
878
    protected function getOrderItemsField()
879
    {
880
        $gridFieldConfig = GridFieldConfigForOrderItems::create();
881
        $source = $this->OrderItems();
882
883
        return new GridField('OrderItems', _t('OrderItems.PLURALNAME', 'Order Items'), $source, $gridFieldConfig);
884
    }
885
886
    /**
887
     * Field to add and edit Modifiers.
888
     *
889
     * @return GridField
890
     */
891
    public function getModifierTableField()
892
    {
893
        $gridFieldConfig = GridFieldConfigForOrderItems::create();
894
        $source = $this->Modifiers();
895
896
        return new GridField('OrderModifiers', _t('OrderItems.PLURALNAME', 'Order Items'), $source, $gridFieldConfig);
897
    }
898
899
    /**
900
     *@return GridField
901
     **/
902
    protected function getBillingAddressField()
903
    {
904
        $this->CreateOrReturnExistingAddress('BillingAddress');
905
        $gridFieldConfig = GridFieldConfig::create()->addComponents(
906
            new GridFieldToolbarHeader(),
907
            new GridFieldSortableHeader(),
908
            new GridFieldDataColumns(),
909
            new GridFieldPaginator(10),
910
            new GridFieldEditButton(),
911
            new GridFieldDetailForm()
912
        );
913
        //$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...
914
        $source = BillingAddress::get()->filter(array('OrderID' => $this->ID));
915
916
        return new GridField('BillingAddress', _t('BillingAddress.SINGULARNAME', 'Billing Address'), $source, $gridFieldConfig);
917
    }
918
919
    /**
920
     *@return GridField
921
     **/
922
    protected function getShippingAddressField()
923
    {
924
        $this->CreateOrReturnExistingAddress('ShippingAddress');
925
        $gridFieldConfig = GridFieldConfig::create()->addComponents(
926
            new GridFieldToolbarHeader(),
927
            new GridFieldSortableHeader(),
928
            new GridFieldDataColumns(),
929
            new GridFieldPaginator(10),
930
            new GridFieldEditButton(),
931
            new GridFieldDetailForm()
932
        );
933
        //$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...
934
        $source = ShippingAddress::get()->filter(array('OrderID' => $this->ID));
935
936
        return new GridField('ShippingAddress', _t('BillingAddress.SINGULARNAME', 'Shipping Address'), $source, $gridFieldConfig);
937
    }
938
939
    /**
940
     * Needs to be public because the OrderStep::getCMSFIelds accesses it.
941
     *
942
     * @param string    $sourceClass
943
     * @param string    $title
944
     *
945
     * @return GridField
946
     **/
947
    public function getOrderStatusLogsTableField(
948
        $sourceClass = 'OrderStatusLog',
949
        $title = ''
950
    ) {
951
        $gridFieldConfig = GridFieldConfig_RecordViewer::create()->addComponents(
952
            new GridFieldAddNewButton('toolbar-header-right'),
953
            new GridFieldDetailForm()
954
        );
955
        $title ? $title : $title = _t('OrderStatusLog.PLURALNAME', 'Order Status Logs');
956
        $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...
957
        $gf = new GridField($sourceClass, $title, $source, $gridFieldConfig);
958
        $gf->setModelClass($sourceClass);
959
960
        return $gf;
961
    }
962
963
    /**
964
     * Needs to be public because the OrderStep::getCMSFIelds accesses it.
965
     *
966
     * @param string    $sourceClass
967
     * @param string    $title
968
     *
969
     * @return GridField
970
     **/
971
    public function getOrderStatusLogsTableFieldEditable(
972
        $sourceClass = 'OrderStatusLog',
973
        $title = ''
974
    ) {
975
        $gf = $this->getOrderStatusLogsTableField($sourceClass, $title);
976
        $gf->getConfig()->addComponents(
977
            new GridFieldEditButton()
978
        );
979
        return $gf;
980
    }
981
982
    /**
983
     * @param string    $sourceClass
984
     * @param string    $title
985
     * @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...
986
     * @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...
987
     *
988
     * @return GridField
989
     **/
990
    protected function getOrderStatusLogsTableField_Archived(
991
        $sourceClass = 'OrderStatusLog',
992
        $title = '',
993
        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...
994
        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...
995
    ) {
996
        $title ? $title : $title = _t('OrderLog.PLURALNAME', 'Order Log');
997
        $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...
998
        if ($sourceClass != 'OrderStatusLog' && class_exists($sourceClass)) {
999
            $source = $source->filter(array('ClassName' => ClassInfo::subclassesFor($sourceClass)));
1000
        }
1001
        $gridField = GridField::create($sourceClass, $title, $source, $config = GridFieldConfig_RelationEditor::create());
1002
        $config->removeComponentsByType('GridFieldAddExistingAutocompleter');
1003
        $config->removeComponentsByType('GridFieldDeleteAction');
1004
1005
        return $gridField;
1006
    }
1007
1008
    /**
1009
     * @return GridField
1010
     **/
1011
    public function getEmailsTableField()
1012
    {
1013
        $gridFieldConfig = GridFieldConfig_RecordViewer::create()->addComponents(
1014
            new GridFieldDetailForm()
1015
        );
1016
1017
        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...
1018
    }
1019
1020
    /**
1021
     * @return GridField
1022
     */
1023
    protected function getPaymentsField()
1024
    {
1025
        $gridFieldConfig = GridFieldConfig_RecordViewer::create()->addComponents(
1026
            new GridFieldDetailForm(),
1027
            new GridFieldEditButton()
1028
        );
1029
1030
        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...
1031
    }
1032
1033
    /**
1034
     * @return OrderStepField
1035
     */
1036
    public function OrderStepField()
1037
    {
1038
        return OrderStepField::create($name = 'MyOrderStep', $this, Member::currentUser());
1039
    }
1040
1041
/*******************************************************
1042
   * 2. MAIN TRANSITION FUNCTIONS
1043
*******************************************************/
1044
1045
    /**
1046
     * init runs on start of a new Order (@see onAfterWrite)
1047
     * it adds all the modifiers to the orders and the starting OrderStep.
1048
     *
1049
     * @param bool $recalculate
1050
     *
1051
     * @return DataObject (Order)
1052
     **/
1053
    public function init($recalculate = false)
1054
    {
1055
        if ($this->IsSubmitted()) {
1056
            user_error('Can not init an order that has been submitted', E_USER_NOTICE);
1057
        } else {
1058
            //to do: check if shop is open....
1059
            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...
1060
                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...
1061
                    $createdOrderStatus = OrderStep::get()->First();
1062
                    if (!$createdOrderStatus) {
1063
                        user_error('No ordersteps have been created', E_USER_WARNING);
1064
                    }
1065
                    $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...
1066
                }
1067
                $createdModifiersClassNames = array();
1068
                $modifiersAsArrayList = new ArrayList();
1069
                $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...
1070
                if ($modifiers->count()) {
1071
                    foreach ($modifiers as $modifier) {
1072
                        $modifiersAsArrayList->push($modifier);
1073
                    }
1074
                }
1075
                if ($modifiersAsArrayList->count()) {
1076
                    foreach ($modifiersAsArrayList as $modifier) {
1077
                        $createdModifiersClassNames[$modifier->ID] = $modifier->ClassName;
1078
                    }
1079
                } 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...
1080
                }
1081
                $modifiersToAdd = EcommerceConfig::get('Order', 'modifiers');
1082
                if (is_array($modifiersToAdd) && count($modifiersToAdd) > 0) {
1083
                    foreach ($modifiersToAdd as $numericKey => $className) {
1084
                        if (!in_array($className, $createdModifiersClassNames)) {
1085
                            if (class_exists($className)) {
1086
                                $modifier = new $className();
1087
                                //only add the ones that should be added automatically
1088
                                if (!$modifier->DoNotAddAutomatically()) {
1089
                                    if (is_a($modifier, 'OrderModifier')) {
1090
                                        $modifier->OrderID = $this->ID;
1091
                                        $modifier->Sort = $numericKey;
1092
                                        //init method includes a WRITE
1093
                                        $modifier->init();
1094
                                        //IMPORTANT - add as has_many relationship  (Attributes can be a modifier OR an OrderItem)
1095
                                        $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...
1096
                                        $modifiersAsArrayList->push($modifier);
1097
                                    }
1098
                                }
1099
                            } else {
1100
                                user_error('reference to a non-existing class: '.$className.' in modifiers', E_USER_NOTICE);
1101
                            }
1102
                        }
1103
                    }
1104
                }
1105
                $this->extend('onInit', $this);
1106
                //careful - this will call "onAfterWrite" again
1107
                $this->write();
1108
            }
1109
        }
1110
1111
        return $this;
1112
    }
1113
1114
    /**
1115
     * @var array
1116
     */
1117
    private static $_try_to_finalise_order_is_running = array();
1118
1119
    /**
1120
     * Goes through the order steps and tries to "apply" the next status to the order.
1121
     *
1122
     * @param bool $runAgain
1123
     * @param bool $fromOrderQueue - is it being called from the OrderProcessQueue (or similar)
1124
     **/
1125
    public function tryToFinaliseOrder($runAgain = false, $fromOrderQueue = false)
1126
    {
1127
        if (empty(self::$_try_to_finalise_order_is_running[$this->ID]) || $runAgain) {
1128
            self::$_try_to_finalise_order_is_running[$this->ID] = true;
1129
1130
            //if the order has been cancelled then we do not process it ...
1131
            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...
1132
                $this->Archive(true);
1133
1134
                return;
1135
            }
1136
            // if it is in the queue it has to run from the queue tasks
1137
            // if it ruins from the queue tasks then it has to be one currently processing.
1138
            $queueObjectSingleton = Injector::inst()->get('OrderProcessQueue');
1139
            if ($myQueueObject = $queueObjectSingleton->getQueueObject($this)) {
1140
                if($fromOrderQueue) {
1141
                    if ( ! $myQueueObject->InProcess) {
1142
                        return;
1143
                    }
1144
                } else {
1145
                    return;
1146
                }
1147
            }
1148
            //a little hack to make sure we do not rely on a stored value
1149
            //of "isSubmitted"
1150
            $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...
1151
            //status of order is being progressed
1152
            $nextStatusID = $this->doNextStatus();
1153
            if ($nextStatusID) {
1154
                $nextStatusObject = OrderStep::get()->byID($nextStatusID);
1155
                if ($nextStatusObject) {
1156
                    $delay = $nextStatusObject->CalculatedDeferTimeInSeconds($this);
1157
                    if ($delay > 0) {
1158
                        if ($nextStatusObject->DeferFromSubmitTime) {
1159
                            $delay = $delay - $this->SecondsSinceBeingSubmitted();
1160
                            if ($delay < 0) {
1161
                                $delay = 0;
1162
                            }
1163
                        }
1164
                        $queueObjectSingleton->AddOrderToQueue(
1165
                            $this,
1166
                            $delay
1167
                        );
1168
                    } else {
1169
                        //status has been completed, so it can be released
1170
                        self::$_try_to_finalise_order_is_running[$this->ID] = false;
1171
                        $this->tryToFinaliseOrder($runAgain, $fromOrderQueue);
1172
                    }
1173
                }
1174
            }
1175
        }
1176
    }
1177
1178
    /**
1179
     * Goes through the order steps and tries to "apply" the next step
1180
     * Step is updated after the other one is completed...
1181
     *
1182
     * @return int (StatusID or false if the next status can not be "applied")
1183
     **/
1184
    public function doNextStatus()
1185
    {
1186
        if ($this->MyStep()->initStep($this)) {
1187
            if ($this->MyStep()->doStep($this)) {
1188
                if ($nextOrderStepObject = $this->MyStep()->nextStep($this)) {
1189
                    $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...
1190
                    $this->write();
1191
1192
                    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...
1193
                }
1194
            }
1195
        }
1196
1197
        return 0;
1198
    }
1199
1200
    /**
1201
     * cancel an order.
1202
     *
1203
     * @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...
1204
     * @param string $reason - (optional) the reason the order is cancelled
1205
     *
1206
     * @return OrderStatusLog_Cancel
1207
     */
1208
    public function Cancel($member = null, $reason = '')
1209
    {
1210
        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...
1211
            //we have a valid member
1212
        } else {
1213
            $member = EcommerceRole::get_default_shop_admin_user();
1214
        }
1215
        if($member) {
1216
            //archive and write
1217
            $this->Archive($avoidWrites = true);
1218
            if($avoidWrites) {
1219
                DB::query('Update "Order" SET CancelledByID = '.$member->ID.' WHERE ID = '.$this->ID.' LIMIT 1;');
1220
            } else {
1221
                $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...
1222
                $this->write();
1223
            }
1224
            //create log ...
1225
            $log = OrderStatusLog_Cancel::create();
1226
            $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...
1227
            $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...
1228
            $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...
1229
            if ($member->IsShopAdmin()) {
1230
                $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...
1231
            }
1232
            $log->write();
1233
            //remove from queue ...
1234
            $queueObjectSingleton = Injector::inst()->get('OrderProcessQueue');
1235
            $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...
1236
            $this->extend('doCancel', $member, $log);
1237
1238
            return $log;
1239
        }
1240
    }
1241
1242
    /**
1243
     * returns true if successful.
1244
     *
1245
     * @param bool $avoidWrites
1246
     *
1247
     * @return bool
1248
     */
1249
    public function Archive($avoidWrites = true)
1250
    {
1251
        $lastOrderStep = OrderStep::get()->Last();
1252
        if ($lastOrderStep) {
1253
            if ($avoidWrites) {
1254
                DB::query('
1255
                    UPDATE "Order"
1256
                    SET "Order"."StatusID" = '.$lastOrderStep->ID.'
1257
                    WHERE "Order"."ID" = '.$this->ID.'
1258
                    LIMIT 1
1259
                ');
1260
1261
                return true;
1262
            } else {
1263
                $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...
1264
                $this->write();
1265
1266
                return true;
1267
            }
1268
        }
1269
1270
        return false;
1271
    }
1272
1273
/*******************************************************
1274
   * 3. STATUS RELATED FUNCTIONS / SHORTCUTS
1275
*******************************************************/
1276
1277
    /**
1278
     * Avoids caching of $this->Status().
1279
     *
1280
     * @return DataObject (current OrderStep)
1281
     */
1282
    public function MyStep()
1283
    {
1284
        $step = null;
1285
        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...
1286
            $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...
1287
        }
1288
        if (!$step) {
1289
            $step = OrderStep::get()->First(); //TODO: this could produce strange results
1290
        }
1291
        if (!$step) {
1292
            $step = OrderStep_Created::create();
1293
        }
1294
        if (!$step) {
1295
            user_error('You need an order step in your Database.');
1296
        }
1297
1298
        return $step;
1299
    }
1300
1301
    /**
1302
     * Return the OrderStatusLog that is relevant to the Order status.
1303
     *
1304
     * @return OrderStatusLog
1305
     */
1306
    public function RelevantLogEntry()
1307
    {
1308
        return $this->MyStep()->RelevantLogEntry($this);
1309
    }
1310
1311
    /**
1312
     * @return OrderStep (current OrderStep that can be seen by customer)
0 ignored issues
show
Documentation introduced by
Should the return type not be OrderStep|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...
1313
     */
1314
    public function CurrentStepVisibleToCustomer()
1315
    {
1316
        $obj = $this->MyStep();
1317
        if ($obj->HideStepFromCustomer) {
1318
            $obj = OrderStep::get()->where('"OrderStep"."Sort" < '.$obj->Sort.' AND "HideStepFromCustomer" = 0')->Last();
1319
            if (!$obj) {
1320
                $obj = OrderStep::get()->First();
1321
            }
1322
        }
1323
1324
        return $obj;
1325
    }
1326
1327
    /**
1328
     * works out if the order is still at the first OrderStep.
1329
     *
1330
     * @return bool
1331
     */
1332
    public function IsFirstStep()
1333
    {
1334
        $firstStep = OrderStep::get()->First();
1335
        $currentStep = $this->MyStep();
1336
        if ($firstStep && $currentStep) {
1337
            if ($firstStep->ID == $currentStep->ID) {
1338
                return true;
1339
            }
1340
        }
1341
1342
        return false;
1343
    }
1344
1345
    /**
1346
     * Is the order still being "edited" by the customer?
1347
     *
1348
     * @return bool
1349
     */
1350
    public function IsInCart()
1351
    {
1352
        return (bool) $this->IsSubmitted() ? false : true;
1353
    }
1354
1355
    /**
1356
     * The order has "passed" the IsInCart phase.
1357
     *
1358
     * @return bool
1359
     */
1360
    public function IsPastCart()
1361
    {
1362
        return (bool) $this->IsInCart() ? false : true;
1363
    }
1364
1365
    /**
1366
     * Are there still steps the order needs to go through?
1367
     *
1368
     * @return bool
1369
     */
1370
    public function IsUncomplete()
1371
    {
1372
        return (bool) $this->MyStep()->ShowAsUncompletedOrder;
1373
    }
1374
1375
    /**
1376
     * Is the order in the :"processing" phaase.?
1377
     *
1378
     * @return bool
1379
     */
1380
    public function IsProcessing()
1381
    {
1382
        return (bool) $this->MyStep()->ShowAsInProcessOrder;
1383
    }
1384
1385
    /**
1386
     * Is the order completed?
1387
     *
1388
     * @return bool
1389
     */
1390
    public function IsCompleted()
1391
    {
1392
        return (bool) $this->MyStep()->ShowAsCompletedOrder;
1393
    }
1394
1395
    /**
1396
     * Has the order been paid?
1397
     * TODO: why do we check if there is a total at all?
1398
     *
1399
     * @return bool
1400
     */
1401
    public function IsPaid()
1402
    {
1403
        if ($this->IsSubmitted()) {
1404
            return (bool) (($this->Total() >= 0) && ($this->TotalOutstanding() <= 0));
1405
        }
1406
1407
        return false;
1408
    }
1409
    /**
1410
     * Has the order been paid?
1411
     * TODO: why do we check if there is a total at all?
1412
     *
1413
     * @return Boolean (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...
1414
     */
1415
    public function IsPaidNice()
1416
    {
1417
        return  DBField::create_field('Boolean', $this->IsPaid());
1418
    }
1419
1420
    /**
1421
     * Has the order been paid?
1422
     * TODO: why do we check if there is a total at all?
1423
     *
1424
     * @return bool
1425
     */
1426
    public function PaymentIsPending()
1427
    {
1428
        if ($this->IsSubmitted()) {
1429
            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...
1430
                //do nothing;
1431
            } 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...
1432
                foreach ($payments as $payment) {
1433
                    if ('Pending' == $payment->Status) {
1434
                        return true;
1435
                    }
1436
                }
1437
            }
1438
        }
1439
1440
        return false;
1441
    }
1442
1443
    /**
1444
     * shows payments that are meaningfull
1445
     * if the order has been paid then only show successful payments.
1446
     *
1447
     * @return DataList
1448
     */
1449
    public function RelevantPayments()
1450
    {
1451
        if ($this->IsPaid()) {
1452
            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...
1453
            //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...
1454
            //	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...
1455
        } else {
1456
            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...
1457
        }
1458
    }
1459
1460
    /**
1461
     * Has the order been cancelled?
1462
     *
1463
     * @return bool
1464
     */
1465
    public function IsCancelled()
1466
    {
1467
        return $this->getIsCancelled();
1468
    }
1469
    public function getIsCancelled()
1470
    {
1471
        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...
1472
    }
1473
1474
    /**
1475
     * Has the order been cancelled by the customer?
1476
     *
1477
     * @return bool
1478
     */
1479
    public function IsCustomerCancelled()
1480
    {
1481
        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...
1482
            return true;
1483
        }
1484
1485
        return false;
1486
    }
1487
1488
    /**
1489
     * Has the order been cancelled by the  administrator?
1490
     *
1491
     * @return bool
1492
     */
1493
    public function IsAdminCancelled()
1494
    {
1495
        if ($this->IsCancelled()) {
1496
            if (!$this->IsCustomerCancelled()) {
1497
                $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...
1498
                if ($admin) {
1499
                    if ($admin->IsShopAdmin()) {
1500
                        return true;
1501
                    }
1502
                }
1503
            }
1504
        }
1505
1506
        return false;
1507
    }
1508
1509
    /**
1510
     * Is the Shop Closed for business?
1511
     *
1512
     * @return bool
1513
     */
1514
    public function ShopClosed()
1515
    {
1516
        return EcomConfig()->ShopClosed;
1517
    }
1518
1519
/*******************************************************
1520
   * 4. LINKING ORDER WITH MEMBER AND ADDRESS
1521
*******************************************************/
1522
1523
    /**
1524
     * Returns a member linked to the order.
1525
     * If a member is already linked, it will return the existing member.
1526
     * Otherwise it will return a new Member.
1527
     *
1528
     * Any new member is NOT written, because we dont want to create a new member unless we have to!
1529
     * We will not add a member to the order unless a new one is created in the checkout
1530
     * OR the member is logged in / logs in.
1531
     *
1532
     * Also note that if a new member is created, it is not automatically written
1533
     *
1534
     * @param bool $forceCreation - if set to true then the member will always be saved in the database.
1535
     *
1536
     * @return Member
1537
     **/
1538
    public function CreateOrReturnExistingMember($forceCreation = false)
1539
    {
1540
        if ($this->IsSubmitted()) {
1541
            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...
1542
        }
1543
        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...
1544
            $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...
1545
        } elseif ($member = Member::currentUser()) {
1546
            if (!$member->IsShopAdmin()) {
1547
                $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...
1548
                $this->write();
1549
            }
1550
        }
1551
        $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...
1552
        if (!$member) {
1553
            $member = new Member();
1554
        }
1555
        if ($member && $forceCreation) {
1556
            $member->write();
1557
        }
1558
1559
        return $member;
1560
    }
1561
1562
    /**
1563
     * Returns either the existing one or a new Order Address...
1564
     * All Orders will have a Shipping and Billing address attached to it.
1565
     * Method used to retrieve object e.g. for $order->BillingAddress(); "BillingAddress" is the method name you can use.
1566
     * If the method name is the same as the class name then dont worry about providing one.
1567
     *
1568
     * @param string $className             - ClassName of the Address (e.g. BillingAddress or ShippingAddress)
1569
     * @param string $alternativeMethodName - method to retrieve Address
1570
     **/
1571
    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...
1572
    {
1573
        if ($this->exists()) {
1574
            $methodName = $className;
1575
            if ($alternativeMethodName) {
1576
                $methodName = $alternativeMethodName;
1577
            }
1578
            if ($this->IsSubmitted()) {
1579
                return $this->$methodName();
1580
            }
1581
            $variableName = $className.'ID';
1582
            $address = null;
1583
            if ($this->$variableName) {
1584
                $address = $this->$methodName();
1585
            }
1586
            if (!$address) {
1587
                $address = new $className();
1588
                if ($member = $this->CreateOrReturnExistingMember()) {
1589
                    if ($member->exists()) {
1590
                        $address->FillWithLastAddressFromMember($member, $write = false);
1591
                    }
1592
                }
1593
            }
1594
            if ($address) {
1595
                if (!$address->exists()) {
1596
                    $address->write();
1597
                }
1598
                if ($address->OrderID != $this->ID) {
1599
                    $address->OrderID = $this->ID;
1600
                    $address->write();
1601
                }
1602
                if ($this->$variableName != $address->ID) {
1603
                    if (!$this->IsSubmitted()) {
1604
                        $this->$variableName = $address->ID;
1605
                        $this->write();
1606
                    }
1607
                }
1608
1609
                return $address;
1610
            }
1611
        }
1612
1613
        return;
1614
    }
1615
1616
    /**
1617
     * Sets the country in the billing and shipping address.
1618
     *
1619
     * @param string $countryCode            - code for the country e.g. NZ
1620
     * @param bool   $includeBillingAddress
1621
     * @param bool   $includeShippingAddress
1622
     **/
1623
    public function SetCountryFields($countryCode, $includeBillingAddress = true, $includeShippingAddress = true)
1624
    {
1625
        if ($this->IsSubmitted()) {
1626
            user_error('Can not change country in submitted order', E_USER_NOTICE);
1627
        } else {
1628
            if ($includeBillingAddress) {
1629
                if ($billingAddress = $this->CreateOrReturnExistingAddress('BillingAddress')) {
1630
                    $billingAddress->SetCountryFields($countryCode);
1631
                }
1632
            }
1633
            if (EcommerceConfig::get('OrderAddress', 'use_separate_shipping_address')) {
1634
                if ($includeShippingAddress) {
1635
                    if ($shippingAddress = $this->CreateOrReturnExistingAddress('ShippingAddress')) {
1636
                        $shippingAddress->SetCountryFields($countryCode);
1637
                    }
1638
                }
1639
            }
1640
        }
1641
    }
1642
1643
    /**
1644
     * Sets the region in the billing and shipping address.
1645
     *
1646
     * @param int $regionID - ID for the region to be set
1647
     **/
1648
    public function SetRegionFields($regionID)
1649
    {
1650
        if ($this->IsSubmitted()) {
1651
            user_error('Can not change country in submitted order', E_USER_NOTICE);
1652
        } else {
1653
            if ($billingAddress = $this->CreateOrReturnExistingAddress('BillingAddress')) {
1654
                $billingAddress->SetRegionFields($regionID);
1655
            }
1656
            if ($this->CanHaveShippingAddress()) {
1657
                if ($shippingAddress = $this->CreateOrReturnExistingAddress('ShippingAddress')) {
1658
                    $shippingAddress->SetRegionFields($regionID);
1659
                }
1660
            }
1661
        }
1662
    }
1663
1664
    /**
1665
     * Stores the preferred currency of the order.
1666
     * IMPORTANTLY we store the exchange rate for future reference...
1667
     *
1668
     * @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...
1669
     */
1670
    public function UpdateCurrency($newCurrency)
1671
    {
1672
        if ($this->IsSubmitted()) {
1673
            user_error('Can not set the currency after the order has been submitted', E_USER_NOTICE);
1674
        } else {
1675
            if (! is_a($newCurrency, Object::getCustomClass('EcommerceCurrency'))) {
1676
                $newCurrency = EcommerceCurrency::default_currency();
1677
            }
1678
            $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...
1679
            $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...
1680
            $this->write();
1681
        }
1682
    }
1683
1684
    /**
1685
     * alias for UpdateCurrency.
1686
     *
1687
     * @param EcommerceCurrency $currency
1688
     */
1689
    public function SetCurrency($currency)
1690
    {
1691
        $this->UpdateCurrency($currency);
1692
    }
1693
1694
/*******************************************************
1695
   * 5. CUSTOMER COMMUNICATION
1696
*******************************************************/
1697
1698
    /**
1699
     * Send the invoice of the order by email.
1700
     *
1701
     * @param string $emailClassName     (optional) class used to send email
1702
     * @param string $subject            (optional) subject for the email
1703
     * @param string $message            (optional) the main message in the email
1704
     * @param bool   $resend             (optional) send the email even if it has been sent before
1705
     * @param bool   $adminOnlyOrToEmail (optional) sends the email to the ADMIN ONLY, if you provide an email, it will go to the email...
1706
     *
1707
     * @return bool TRUE on success, FALSE on failure
1708
     */
1709
    public function sendEmail(
1710
        $emailClassName = 'Order_InvoiceEmail',
1711
        $subject = '',
1712
        $message = '',
1713
        $resend = false,
1714
        $adminOnlyOrToEmail = false
1715
    ) {
1716
        return $this->prepareAndSendEmail(
1717
            $emailClassName,
1718
            $subject,
1719
            $message,
1720
            $resend,
1721
            $adminOnlyOrToEmail
1722
        );
1723
    }
1724
1725
    /**
1726
     * Sends a message to the shop admin ONLY and not to the customer
1727
     * This can be used by ordersteps and orderlogs to notify the admin of any potential problems.
1728
     *
1729
     * @param string         $emailClassName       - (optional) template to be used ...
1730
     * @param string         $subject              - (optional) subject for the email
1731
     * @param string         $message              - (optional) message to be added with the email
1732
     * @param bool           $resend               - (optional) can it be sent twice?
1733
     * @param bool | string  $adminOnlyOrToEmail   - (optional) sends the email to the ADMIN ONLY, if you provide an email, it will go to the email...
1734
     *
1735
     * @return bool TRUE for success, FALSE for failure (not tested)
1736
     */
1737
    public function sendAdminNotification(
1738
        $emailClassName = 'Order_ErrorEmail',
1739
        $subject = '',
1740
        $message = '',
1741
        $resend = false,
1742
        $adminOnlyOrToEmail = true
1743
    ) {
1744
        return $this->prepareAndSendEmail(
1745
            $emailClassName,
1746
            $subject,
1747
            $message,
1748
            $resend,
1749
            $adminOnlyOrToEmail
1750
        );
1751
    }
1752
1753
    /**
1754
     * returns the order formatted as an email.
1755
     *
1756
     * @param string $emailClassName - template to use.
1757
     * @param string $subject        - (optional) the subject (which can be used as title in email)
1758
     * @param string $message        - (optional) the additional message
1759
     *
1760
     * @return string (html)
1761
     */
1762
    public function renderOrderInEmailFormat(
1763
        $emailClassName,
1764
        $subject = '',
1765
        $message = ''
1766
    )
1767
    {
1768
        $arrayData = $this->createReplacementArrayForEmail($subject, $message);
1769
        Config::nest();
1770
        Config::inst()->update('SSViewer', 'theme_enabled', true);
1771
        $html = $arrayData->renderWith($emailClassName);
1772
        Config::unnest();
1773
1774
        return Order_Email::emogrify_html($html);
1775
    }
1776
1777
    /**
1778
     * Send a mail of the order to the client (and another to the admin).
1779
     *
1780
     * @param string         $emailClassName       - (optional) template to be used ...
1781
     * @param string         $subject              - (optional) subject for the email
1782
     * @param string         $message              - (optional) message to be added with the email
1783
     * @param bool           $resend               - (optional) can it be sent twice?
1784
     * @param bool | string  $adminOnlyOrToEmail   - (optional) sends the email to the ADMIN ONLY, if you provide an email, it will go to the email...
1785
     *
1786
     * @return bool TRUE for success, FALSE for failure (not tested)
1787
     */
1788
    protected function prepareAndSendEmail(
1789
        $emailClassName,
1790
        $subject,
1791
        $message,
1792
        $resend = false,
1793
        $adminOnlyOrToEmail = false
1794
    ) {
1795
        $arrayData = $this->createReplacementArrayForEmail($subject, $message);
1796
        $from = Order_Email::get_from_email();
1797
        //why are we using this email and NOT the member.EMAIL?
1798
        //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...
1799
        if ($adminOnlyOrToEmail) {
1800
            if (filter_var($adminOnlyOrToEmail, FILTER_VALIDATE_EMAIL)) {
1801
                $to = $adminOnlyOrToEmail;
1802
                // invalid e-mail address
1803
            } else {
1804
                $to = Order_Email::get_from_email();
1805
            }
1806
        } else {
1807
            $to = $this->getOrderEmail();
1808
        }
1809
        if ($from && $to) {
1810
            $email = new $emailClassName();
1811
            if (!(is_a($email, Object::getCustomClass('Email')))) {
1812
                user_error('No correct email class provided.', E_USER_ERROR);
1813
            }
1814
            $email->setFrom($from);
1815
            $email->setTo($to);
1816
            //we take the subject from the Array Data, just in case it has been adjusted.
1817
            $email->setSubject($arrayData->getField('Subject'));
1818
            //we also see if a CC and a BCC have been added
1819
            ;
1820
            if ($cc = $arrayData->getField('CC')) {
1821
                $email->setCc($cc);
1822
            }
1823
            if ($bcc = $arrayData->getField('BCC')) {
1824
                $email->setBcc($bcc);
1825
            }
1826
            $email->populateTemplate($arrayData);
1827
            // This might be called from within the CMS,
1828
            // so we need to restore the theme, just in case
1829
            // templates within the theme exist
1830
            Config::nest();
1831
            Config::inst()->update('SSViewer', 'theme_enabled', true);
1832
            $email->setOrder($this);
1833
            $email->setResend($resend);
1834
            $result = $email->send(null);
1835
            Config::unnest();
1836
            if (Director::isDev()) {
1837
                return true;
1838
            } else {
1839
                return $result;
1840
            }
1841
        }
1842
1843
        return false;
1844
    }
1845
1846
    /**
1847
     * returns the Data that can be used in the body of an order Email
1848
     * we add the subject here so that the subject, for example, can be added to the <title>
1849
     * of the email template.
1850
     * we add the subject here so that the subject, for example, can be added to the <title>
1851
     * of the email template.
1852
     *
1853
     * @param string $subject  - (optional) subject for email
1854
     * @param string $message  - (optional) the additional message
1855
     *
1856
     * @return ArrayData
1857
     *                   - Subject - EmailSubject
1858
     *                   - Message - specific message for this order
1859
     *                   - Message - custom message
1860
     *                   - OrderStepMessage - generic message for step
1861
     *                   - Order
1862
     *                   - EmailLogo
1863
     *                   - ShopPhysicalAddress
1864
     *                   - CurrentDateAndTime
1865
     *                   - BaseURL
1866
     *                   - CC
1867
     *                   - BCC
1868
     */
1869
    protected function createReplacementArrayForEmail($subject = '', $message = '')
1870
    {
1871
        $step = $this->MyStep();
1872
        $config = $this->EcomConfig();
1873
        $replacementArray = array();
1874
        //set subject
1875
        if ( ! $subject) {
1876
            $subject = $step->EmailSubject;
1877
        }
1878
        if( ! $message) {
1879
            $message = $step->CustomerMessage;
1880
        }
1881
        $subject = str_replace('[OrderNumber]', $this->ID, $subject);
1882
        //set other variables
1883
        $replacementArray['Subject'] = $subject;
1884
        $replacementArray['To'] = '';
1885
        $replacementArray['CC'] = '';
1886
        $replacementArray['BCC'] = '';
1887
        $replacementArray['OrderStepMessage'] = $message;
1888
        $replacementArray['Order'] = $this;
1889
        $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...
1890
        $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...
1891
        $replacementArray['CurrentDateAndTime'] = DBField::create_field('SS_Datetime', 'Now');
1892
        $replacementArray['BaseURL'] = Director::baseURL();
1893
        $arrayData = ArrayData::create($replacementArray);
1894
        $this->extend('updateReplacementArrayForEmail', $arrayData);
1895
1896
        return $arrayData;
1897
    }
1898
1899
/*******************************************************
1900
   * 6. ITEM MANAGEMENT
1901
*******************************************************/
1902
1903
    /**
1904
     * returns a list of Order Attributes by type.
1905
     *
1906
     * @param array | String $types
1907
     *
1908
     * @return ArrayList
1909
     */
1910
    public function getOrderAttributesByType($types)
1911
    {
1912
        if (!is_array($types) && is_string($types)) {
1913
            $types = array($types);
1914
        }
1915
        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...
1916
            user_error('wrong parameter (types) provided in Order::getOrderAttributesByTypes');
1917
        }
1918
        $al = new ArrayList();
1919
        $items = $this->Items();
1920
        foreach ($items as $item) {
1921
            if (in_array($item->OrderAttributeType(), $types)) {
1922
                $al->push($item);
1923
            }
1924
        }
1925
        $modifiers = $this->Modifiers();
1926
        foreach ($modifiers as $modifier) {
1927
            if (in_array($modifier->OrderAttributeType(), $types)) {
1928
                $al->push($modifier);
1929
            }
1930
        }
1931
1932
        return $al;
1933
    }
1934
1935
    /**
1936
     * Returns the items of the order.
1937
     * Items are the order items (products) and NOT the modifiers (discount, tax, etc...).
1938
     *
1939
     * N. B. this method returns Order Items
1940
     * also see Buaybles
1941
1942
     *
1943
     * @param string filter - where statement to exclude certain items OR ClassName (e.g. 'TaxModifier')
1944
     *
1945
     * @return DataList (OrderItems)
1946
     */
1947
    public function Items($filterOrClassName = '')
1948
    {
1949
        if (!$this->exists()) {
1950
            $this->write();
1951
        }
1952
1953
        return $this->itemsFromDatabase($filterOrClassName);
1954
    }
1955
1956
    /**
1957
     * @alias function of Items
1958
     *
1959
     * N. B. this method returns Order Items
1960
     * also see Buaybles
1961
     *
1962
     * @param string filter - where statement to exclude certain items.
1963
     * @alias for Items
1964
     * @return DataList (OrderItems)
1965
     */
1966
    public function OrderItems($filterOrClassName = '')
1967
    {
1968
        return $this->Items($filterOrClassName);
1969
    }
1970
1971
    /**
1972
     * returns the buyables asscoiated with the order items.
1973
     *
1974
     * NB. this method retursn buyables
1975
     *
1976
     * @param string filter - where statement to exclude certain items.
1977
     *
1978
     * @return ArrayList (Buyables)
1979
     */
1980
    public function Buyables($filterOrClassName = '')
1981
    {
1982
        $items = $this->Items($filterOrClassName);
1983
        $arrayList = new ArrayList();
1984
        foreach ($items as $item) {
1985
            $arrayList->push($item->Buyable());
1986
        }
1987
1988
        return $arrayList;
1989
    }
1990
1991
    /**
1992
     * Return all the {@link OrderItem} instances that are
1993
     * available as records in the database.
1994
     *
1995
     * @param string filter - where statement to exclude certain items,
1996
     *   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)
1997
     *
1998
     * @return DataList (OrderItems)
1999
     */
2000
    protected function itemsFromDatabase($filterOrClassName = '')
2001
    {
2002
        $className = 'OrderItem';
2003
        $extrafilter = '';
2004
        if ($filterOrClassName) {
2005
            if (class_exists($filterOrClassName)) {
2006
                $className = $filterOrClassName;
2007
            } else {
2008
                $extrafilter = " AND $filterOrClassName";
2009
            }
2010
        }
2011
2012
        return $className::get()->filter(array('OrderID' => $this->ID))->where($extrafilter);
2013
    }
2014
2015
    /**
2016
     * @alias for Modifiers
2017
     *
2018
     * @return DataList (OrderModifiers)
2019
     */
2020
    public function OrderModifiers()
2021
    {
2022
        return $this->Modifiers();
2023
    }
2024
2025
    /**
2026
     * Returns the modifiers of the order, if it hasn't been saved yet
2027
     * it returns the modifiers from session, if it has, it returns them
2028
     * from the DB entry. ONLY USE OUTSIDE ORDER.
2029
     *
2030
     * @param string filter - where statement to exclude certain items OR ClassName (e.g. 'TaxModifier')
2031
     *
2032
     * @return DataList (OrderModifiers)
2033
     */
2034
    public function Modifiers($filterOrClassName = '')
2035
    {
2036
        return $this->modifiersFromDatabase($filterOrClassName);
2037
    }
2038
2039
    /**
2040
     * Get all {@link OrderModifier} instances that are
2041
     * available as records in the database.
2042
     * NOTE: includes REMOVED Modifiers, so that they do not get added again...
2043
     *
2044
     * @param string filter - where statement to exclude certain items OR ClassName (e.g. 'TaxModifier')
2045
     *
2046
     * @return DataList (OrderModifiers)
2047
     */
2048
    protected function modifiersFromDatabase($filterOrClassName = '')
2049
    {
2050
        $className = 'OrderModifier';
2051
        $extrafilter = '';
2052
        if ($filterOrClassName) {
2053
            if (class_exists($filterOrClassName)) {
2054
                $className = $filterOrClassName;
2055
            } else {
2056
                $extrafilter = " AND $filterOrClassName";
2057
            }
2058
        }
2059
2060
        return $className::get()->where('"OrderAttribute"."OrderID" = '.$this->ID." $extrafilter");
2061
    }
2062
2063
    /**
2064
     * Calculates and updates all the order attributes.
2065
     *
2066
     * @param bool $recalculate - run it, even if it has run already
2067
     */
2068
    public function calculateOrderAttributes($recalculate = false)
2069
    {
2070
        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...
2071
            //submitted orders are NEVER recalculated.
2072
            //they are set in stone.
2073
        } elseif (self::get_needs_recalculating($this->ID) || $recalculate) {
2074
            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...
2075
                $this->ensureCorrectExchangeRate();
2076
                $this->calculateOrderItems($recalculate);
2077
                $this->calculateModifiers($recalculate);
2078
                $this->extend('onCalculateOrder');
2079
            }
2080
        }
2081
    }
2082
2083
    /**
2084
     * Calculates and updates all the product items.
2085
     *
2086
     * @param bool $recalculate - run it, even if it has run already
2087
     */
2088
    protected function calculateOrderItems($recalculate = false)
2089
    {
2090
        //check if order has modifiers already
2091
        //check /re-add all non-removable ones
2092
        //$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...
2093
        $orderItems = $this->itemsFromDatabase();
2094
        if ($orderItems->count()) {
2095
            foreach ($orderItems as $orderItem) {
2096
                if ($orderItem) {
2097
                    $orderItem->runUpdate($recalculate);
2098
                }
2099
            }
2100
        }
2101
        $this->extend('onCalculateOrderItems', $orderItems);
2102
    }
2103
2104
    /**
2105
     * Calculates and updates all the modifiers.
2106
     *
2107
     * @param bool $recalculate - run it, even if it has run already
2108
     */
2109
    protected function calculateModifiers($recalculate = false)
2110
    {
2111
        $createdModifiers = $this->modifiersFromDatabase();
2112
        if ($createdModifiers->count()) {
2113
            foreach ($createdModifiers as $modifier) {
2114
                if ($modifier) {
2115
                    $modifier->runUpdate($recalculate);
2116
                }
2117
            }
2118
        }
2119
        $this->extend('onCalculateModifiers', $createdModifiers);
2120
    }
2121
2122
    /**
2123
     * Returns the subtotal of the modifiers for this order.
2124
     * If a modifier appears in the excludedModifiers array, it is not counted.
2125
     *
2126
     * @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...
2127
     * @param bool         $stopAtExcludedModifier - when this flag is TRUE, we stop adding the modifiers when we reach an excluded modifier.
2128
     *
2129
     * @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...
2130
     */
2131
    public function ModifiersSubTotal($excluded = null, $stopAtExcludedModifier = false)
2132
    {
2133
        $total = 0;
2134
        $modifiers = $this->Modifiers();
2135
        if ($modifiers->count()) {
2136
            foreach ($modifiers as $modifier) {
2137
                if (!$modifier->IsRemoved()) { //we just double-check this...
2138
                    if (is_array($excluded) && in_array($modifier->ClassName, $excluded)) {
2139
                        if ($stopAtExcludedModifier) {
2140
                            break;
2141
                        }
2142
                        //do the next modifier
2143
                        continue;
2144
                    } elseif (is_string($excluded) && ($modifier->ClassName == $excluded)) {
2145
                        if ($stopAtExcludedModifier) {
2146
                            break;
2147
                        }
2148
                        //do the next modifier
2149
                        continue;
2150
                    }
2151
                    $total += $modifier->CalculationTotal();
2152
                }
2153
            }
2154
        }
2155
2156
        return $total;
2157
    }
2158
2159
    /**
2160
     * returns a modifier that is an instanceof the classname
2161
     * it extends.
2162
     *
2163
     * @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...
2164
     *
2165
     * @return DataObject (OrderModifier)
2166
     **/
2167
    public function RetrieveModifier($className)
2168
    {
2169
        $modifiers = $this->Modifiers();
2170
        if ($modifiers->count()) {
2171
            foreach ($modifiers as $modifier) {
2172
                if (is_a($modifier, Object::getCustomClass($className))) {
2173
                    return $modifier;
2174
                }
2175
            }
2176
        }
2177
    }
2178
2179
/*******************************************************
2180
   * 7. CRUD METHODS (e.g. canView, canEdit, canDelete, etc...)
2181
*******************************************************/
2182
2183
    /**
2184
     * @param Member $member
2185
     *
2186
     * @return DataObject (Member)
2187
     **/
2188
     //TODO: please comment why we make use of this function
2189
    protected function getMemberForCanFunctions(Member $member = null)
2190
    {
2191
        if (!$member) {
2192
            $member = Member::currentUser();
2193
        }
2194
        if (!$member) {
2195
            $member = new Member();
2196
            $member->ID = 0;
2197
        }
2198
2199
        return $member;
2200
    }
2201
2202
    /**
2203
     * @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...
2204
     *
2205
     * @return bool
2206
     **/
2207
    public function canCreate($member = null)
2208
    {
2209
        $member = $this->getMemberForCanFunctions($member);
2210
        $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...
2211
        if ($extended !== null) {
2212
            return $extended;
2213
        }
2214
        if ($member->exists()) {
2215
            return $member->IsShopAdmin();
2216
        }
2217
    }
2218
2219
    /**
2220
     * Standard SS method - can the current member view this order?
2221
     *
2222
     * @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...
2223
     *
2224
     * @return bool
2225
     **/
2226
    public function canView($member = null)
2227
    {
2228
        if (!$this->exists()) {
2229
            return true;
2230
        }
2231
        $member = $this->getMemberForCanFunctions($member);
2232
        //check if this has been "altered" in any DataExtension
2233
        $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...
2234
        if ($extended !== null) {
2235
            return $extended;
2236
        }
2237
        //is the member is a shop admin they can always view it
2238
        if (EcommerceRole::current_member_is_shop_admin($member)) {
2239
            return true;
2240
        }
2241
2242
        //is the member is a shop assistant they can always view it
2243
        if (EcommerceRole::current_member_is_shop_assistant($member)) {
2244
            return true;
2245
        }
2246
        //if the current member OWNS the order, (s)he can always view it.
2247
        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...
2248
            return true;
2249
        }
2250
        //it is the current order
2251
        if ($this->IsInSession()) {
2252
            //we do some additional CHECKS for session hackings!
2253
            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...
2254
                //can't view the order of another member!
2255
                //shop admin exemption is already captured.
2256
                //this is always true
2257
                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...
2258
                    return false;
2259
                }
2260
            } else {
2261
                //order belongs to someone, but current user is NOT logged in...
2262
                //this is allowed!
2263
                //the reason it is allowed is because we want to be able to
2264
                //add order to non-existing member
2265
                return true;
2266
            }
2267
        }
2268
2269
        return false;
2270
    }
2271
2272
    /**
2273
     * @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...
2274
     * @return bool
2275
     */
2276
    public function canOverrideCanView($member = null)
2277
    {
2278
        if ($this->canView($member)) {
2279
            //can view overrides any concerns
2280
            return true;
2281
        } else {
2282
            $tsOrder = strtotime($this->LastEdited);
2283
            $tsNow = time();
2284
            $minutes = EcommerceConfig::get('Order', 'minutes_an_order_can_be_viewed_without_logging_in');
2285
            if ($minutes && ((($tsNow - $tsOrder) / 60) < $minutes)) {
2286
2287
                //has the order been edited recently?
2288
                return true;
2289
            } elseif ($orderStep = $this->MyStep()) {
2290
2291
                // order is being processed ...
2292
                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...
2293
            }
2294
        }
2295
        return false;
2296
    }
2297
2298
    /**
2299
     * @return bool
2300
     */
2301
    public function IsInSession()
2302
    {
2303
        $orderInSession = ShoppingCart::session_order();
2304
2305
        return $orderInSession && $this->ID && $this->ID == $orderInSession->ID;
2306
    }
2307
2308
    /**
2309
     * returns a pseudo random part of the session id.
2310
     *
2311
     * @param int $size
2312
     *
2313
     * @return string
2314
     */
2315
    public function LessSecureSessionID($size = 7, $start = null)
2316
    {
2317
        if (!$start || $start < 0 || $start > (32 - $size)) {
2318
            $start = 0;
2319
        }
2320
2321
        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...
2322
    }
2323
    /**
2324
     *
2325
     * @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...
2326
     *
2327
     * @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...
2328
     **/
2329
    public function canViewAdminStuff($member = null)
2330
    {
2331
        $member = $this->getMemberForCanFunctions($member);
2332
        $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...
2333
        if ($extended !== null) {
2334
            return $extended;
2335
        }
2336
        if (Permission::checkMember($member, Config::inst()->get('EcommerceRole', 'admin_permission_code'))) {
2337
            return true;
2338
        }
2339
    }
2340
2341
    /**
2342
     * if we set canEdit to false then we
2343
     * can not see the child records
2344
     * Basically, you can edit when you can view and canEdit (even as a customer)
2345
     * Or if you are a Shop Admin you can always edit.
2346
     * Otherwise it is false...
2347
     *
2348
     * @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...
2349
     *
2350
     * @return bool
2351
     **/
2352
    public function canEdit($member = null)
2353
    {
2354
        $member = $this->getMemberForCanFunctions($member);
2355
        $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...
2356
        if ($extended !== null) {
2357
            return $extended;
2358
        }
2359
        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...
2360
            return true;
2361
        }
2362
        if (Permission::checkMember($member, Config::inst()->get('EcommerceRole', 'admin_permission_code'))) {
2363
            return true;
2364
        }
2365
        //is the member is a shop assistant they can always view it
2366
        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...
2367
            return true;
2368
        }
2369
        return false;
2370
    }
2371
2372
    /**
2373
     * is the order ready to go through to the
2374
     * checkout process.
2375
     *
2376
     * This method checks all the order items and order modifiers
2377
     * If any of them need immediate attention then this is done
2378
     * first after which it will go through to the checkout page.
2379
     *
2380
     * @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...
2381
     *
2382
     * @return bool
2383
     **/
2384
    public function canCheckout(Member $member = null)
2385
    {
2386
        $member = $this->getMemberForCanFunctions($member);
2387
        $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...
2388
        if ($extended !== null) {
2389
            return $extended;
2390
        }
2391
        $submitErrors = $this->SubmitErrors();
2392
        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...
2393
            return false;
2394
        }
2395
2396
        return true;
2397
    }
2398
2399
    /**
2400
     * Can the order be submitted?
2401
     * this method can be used to stop an order from being submitted
2402
     * due to something not being completed or done.
2403
     *
2404
     * @see Order::SubmitErrors
2405
     *
2406
     * @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...
2407
     *
2408
     * @return bool
2409
     **/
2410
    public function canSubmit(Member $member = null)
2411
    {
2412
        $member = $this->getMemberForCanFunctions($member);
2413
        $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...
2414
        if ($extended !== null) {
2415
            return $extended;
2416
        }
2417
        if ($this->IsSubmitted()) {
2418
            return false;
2419
        }
2420
        $submitErrors = $this->SubmitErrors();
2421
        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...
2422
            return false;
2423
        }
2424
2425
        return true;
2426
    }
2427
2428
    /**
2429
     * Can a payment be made for this Order?
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 canPay(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->IsPaid() || $this->IsCancelled() || $this->PaymentIsPending()) {
2443
            return false;
2444
        }
2445
2446
        return $this->MyStep()->CustomerCanPay;
2447
    }
2448
2449
    /**
2450
     * Can the given member cancel this order?
2451
     *
2452
     * @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...
2453
     *
2454
     * @return bool
2455
     **/
2456
    public function canCancel(Member $member = null)
2457
    {
2458
        //if it is already cancelled it can not be cancelled again
2459
        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...
2460
            return false;
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 (EcommerceRole::current_member_can_process_orders($member)) {
2468
            return true;
2469
        }
2470
2471
        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...
2472
    }
2473
2474
    /**
2475
     * @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...
2476
     *
2477
     * @return bool
2478
     **/
2479
    public function canDelete($member = null)
2480
    {
2481
        $member = $this->getMemberForCanFunctions($member);
2482
        $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...
2483
        if ($extended !== null) {
2484
            return $extended;
2485
        }
2486
        if ($this->IsSubmitted()) {
2487
            return false;
2488
        }
2489
        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...
2490
            return true;
2491
        }
2492
2493
        return false;
2494
    }
2495
2496
    /**
2497
     * Returns all the order logs that the current member can view
2498
     * i.e. some order logs can only be viewed by the admin (e.g. suspected fraud orderlog).
2499
     *
2500
     * @return ArrayList (OrderStatusLogs)
2501
     **/
2502
    public function CanViewOrderStatusLogs()
2503
    {
2504
        $canViewOrderStatusLogs = new ArrayList();
2505
        $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...
2506
        foreach ($logs as $log) {
2507
            if ($log->canView()) {
2508
                $canViewOrderStatusLogs->push($log);
2509
            }
2510
        }
2511
2512
        return $canViewOrderStatusLogs;
2513
    }
2514
2515
    /**
2516
     * returns all the logs that can be viewed by the customer.
2517
     *
2518
     * @return ArrayList (OrderStausLogs)
2519
     */
2520
    public function CustomerViewableOrderStatusLogs()
2521
    {
2522
        $customerViewableOrderStatusLogs = new ArrayList();
2523
        $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...
2524
        if ($logs) {
2525
            foreach ($logs as $log) {
2526
                if (!$log->InternalUseOnly) {
2527
                    $customerViewableOrderStatusLogs->push($log);
2528
                }
2529
            }
2530
        }
2531
2532
        return $customerViewableOrderStatusLogs;
2533
    }
2534
2535
/*******************************************************
2536
   * 8. GET METHODS (e.g. Total, SubTotal, Title, etc...)
2537
*******************************************************/
2538
2539
    /**
2540
     * returns the email to be used for customer communication.
2541
     *
2542
     * @return string
2543
     */
2544
    public function OrderEmail()
2545
    {
2546
        return $this->getOrderEmail();
2547
    }
2548
    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...
2549
    {
2550
        $email = '';
2551
        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...
2552
            $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...
2553
        }
2554
        if (! $email) {
2555
            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...
2556
                $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...
2557
            }
2558
        }
2559
        $extendedEmail = $this->extend('updateOrderEmail', $email);
2560
        if ($extendedEmail !== null && is_array($extendedEmail) && count($extendedEmail)) {
2561
            $email = implode(';', $extendedEmail);
2562
        }
2563
2564
        return $email;
2565
    }
2566
2567
    /**
2568
     * Returns true if there is a prink or email link.
2569
     *
2570
     * @return bool
2571
     */
2572
    public function HasPrintOrEmailLink()
2573
    {
2574
        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...
2575
    }
2576
2577
    /**
2578
     * returns the absolute link to the order that can be used in the customer communication (email).
2579
     *
2580
     * @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...
2581
     */
2582
    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...
2583
    {
2584
        return $this->getEmailLink();
2585
    }
2586
    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...
2587
    {
2588
        if (!isset($_REQUEST['print'])) {
2589
            if ($this->IsSubmitted()) {
2590
                return Director::AbsoluteURL(OrderConfirmationPage::get_email_link($this->ID, $this->MyStep()->getEmailClassName(), $actuallySendEmail = true));
2591
            }
2592
        }
2593
    }
2594
2595
    /**
2596
     * returns the absolute link to the order for printing.
2597
     *
2598
     * @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...
2599
     */
2600
    public function PrintLink()
2601
    {
2602
        return $this->getPrintLink();
2603
    }
2604
    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...
2605
    {
2606
        if (!isset($_REQUEST['print'])) {
2607
            if ($this->IsSubmitted()) {
2608
                return Director::AbsoluteURL(OrderConfirmationPage::get_order_link($this->ID)).'?print=1';
2609
            }
2610
        }
2611
    }
2612
2613
    /**
2614
     * returns the absolute link to the order for printing.
2615
     *
2616
     * @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...
2617
     */
2618
    public function PackingSlipLink()
2619
    {
2620
        return $this->getPackingSlipLink();
2621
    }
2622
    public function getPackingSlipLink()
2623
    {
2624
        if ($this->IsSubmitted()) {
2625
            return Director::AbsoluteURL(OrderConfirmationPage::get_order_link($this->ID)).'?packingslip=1';
2626
        }
2627
    }
2628
2629
    /**
2630
     * returns the absolute link that the customer can use to retrieve the email WITHOUT logging in.
2631
     *
2632
     * @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...
2633
     */
2634
    public function RetrieveLink()
2635
    {
2636
        return $this->getRetrieveLink();
2637
    }
2638
2639
    public function getRetrieveLink()
2640
    {
2641
        //important to recalculate!
2642
        if ($this->IsSubmitted($recalculate = true)) {
2643
            //add session ID if not added yet...
2644
            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...
2645
                $this->write();
2646
            }
2647
2648
            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...
2649
        } else {
2650
            return Director::AbsoluteURL('/shoppingcart/loadorder/'.$this->ID.'/');
2651
        }
2652
    }
2653
2654
    public function ShareLink()
2655
    {
2656
        return $this->getShareLink();
2657
    }
2658
2659
    public function getShareLink()
2660
    {
2661
        $orderItems = $this->itemsFromDatabase();
2662
        $action = 'share';
2663
        $array = array();
2664
        foreach ($orderItems as $orderItem) {
2665
            $array[] = implode(
2666
                ',',
2667
                array(
2668
                    $orderItem->BuyableClassName,
2669
                    $orderItem->BuyableID,
2670
                    $orderItem->Quantity
2671
                )
2672
            );
2673
        }
2674
2675
        return Director::AbsoluteURL(CartPage::find_link($action.'/'.implode('-', $array)));
2676
    }
2677
2678
    /**
2679
     * @alias for getFeedbackLink
2680
     * @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...
2681
     */
2682
    public function FeedbackLink()
2683
    {
2684
        return $this->getFeedbackLink();
2685
    }
2686
2687
    /**
2688
     * @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...
2689
     */
2690
    public function getFeedbackLink()
2691
    {
2692
        $orderConfirmationPage = OrderConfirmationPage::get()->first();
2693
        if($orderConfirmationPage->IsFeedbackEnabled) {
2694
2695
            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...
2696
        }
2697
    }
2698
2699
    /**
2700
     * link to delete order.
2701
     *
2702
     * @return string
2703
     */
2704
    public function DeleteLink()
2705
    {
2706
        return $this->getDeleteLink();
2707
    }
2708
    public function getDeleteLink()
2709
    {
2710
        if ($this->canDelete()) {
2711
            return ShoppingCart_Controller::delete_order_link($this->ID);
2712
        } else {
2713
            return '';
2714
        }
2715
    }
2716
2717
    /**
2718
     * link to copy order.
2719
     *
2720
     * @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...
2721
     */
2722
    public function CopyOrderLink()
2723
    {
2724
        return $this->getCopyOrderLink();
2725
    }
2726
    public function getCopyOrderLink()
2727
    {
2728
        if ($this->canView() && $this->IsSubmitted()) {
2729
            return ShoppingCart_Controller::copy_order_link($this->ID);
2730
        } else {
2731
            return '';
2732
        }
2733
    }
2734
2735
    /**
2736
     * A "Title" for the order, which summarises the main details (date, and customer) in a string.
2737
     *
2738
     * @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...
2739
     * @param bool   $includeName - e.g. by Mr Johnson
2740
     *
2741
     * @return string
2742
     **/
2743
    public function Title($dateFormat = null, $includeName = false)
2744
    {
2745
        return $this->getTitle($dateFormat, $includeName);
2746
    }
2747
    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...
2748
    {
2749
        if ($this->exists()) {
2750
            if ($dateFormat === null) {
2751
                $dateFormat = EcommerceConfig::get('Order', 'date_format_for_title');
2752
            }
2753
            if ($includeName === null) {
2754
                $includeName = EcommerceConfig::get('Order', 'include_customer_name_in_title');
2755
            }
2756
            $title = $this->i18n_singular_name()." #".number_format($this->ID);
2757
            if ($dateFormat) {
2758
                if ($submissionLog = $this->SubmissionLog()) {
2759
                    $dateObject = $submissionLog->dbObject('Created');
2760
                    $placed = _t('Order.PLACED', 'placed');
2761
                } else {
2762
                    $dateObject = $this->dbObject('Created');
2763
                    $placed = _t('Order.STARTED', 'started');
2764
                }
2765
                $title .= ' - '.$placed.' '.$dateObject->Format($dateFormat);
2766
            }
2767
            $name = '';
2768
            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...
2769
                $name = ' - '._t('Order.CANCELLED', 'CANCELLED');
2770
            }
2771
            if ($includeName) {
2772
                $by = _t('Order.BY', 'by');
2773
                if (!$name) {
2774
                    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...
2775
                        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...
2776
                            $name = ' - '.$by.' '.$billingAddress->Prefix.' '.$billingAddress->FirstName.' '.$billingAddress->Surname;
2777
                        }
2778
                    }
2779
                }
2780
                if (!$name) {
2781
                    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...
2782
                        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...
2783
                            if ($member->exists()) {
2784
                                if ($memberName = $member->getName()) {
2785
                                    if (!trim($memberName)) {
2786
                                        $memberName = _t('Order.ANONYMOUS', 'anonymous');
2787
                                    }
2788
                                    $name = ' - '.$by.' '.$memberName;
2789
                                }
2790
                            }
2791
                        }
2792
                    }
2793
                }
2794
            }
2795
            $title .= $name;
2796
        } else {
2797
            $title = _t('Order.NEW', 'New').' '.$this->i18n_singular_name();
2798
        }
2799
        $extendedTitle = $this->extend('updateTitle', $title);
2800
        if ($extendedTitle !== null && is_array($extendedTitle) && count($extendedTitle)) {
2801
            $title = implode('; ', $extendedTitle);
2802
        }
2803
2804
        return $title;
2805
    }
2806
2807
    /**
2808
     * Returns the subtotal of the items for this order.
2809
     *
2810
     * @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...
2811
     */
2812
    public function SubTotal()
2813
    {
2814
        return $this->getSubTotal();
2815
    }
2816
    public function getSubTotal()
2817
    {
2818
        $result = 0;
2819
        $items = $this->Items();
2820
        if ($items->count()) {
2821
            foreach ($items as $item) {
2822
                if (is_a($item, Object::getCustomClass('OrderAttribute'))) {
2823
                    $result += $item->Total();
2824
                }
2825
            }
2826
        }
2827
2828
        return $result;
2829
    }
2830
2831
    /**
2832
     * @return Currency (DB Object)
2833
     **/
2834
    public function SubTotalAsCurrencyObject()
2835
    {
2836
        return DBField::create_field('Currency', $this->SubTotal());
2837
    }
2838
2839
    /**
2840
     * @return Money
2841
     **/
2842
    public function SubTotalAsMoney()
2843
    {
2844
        return $this->getSubTotalAsMoney();
2845
    }
2846
    public function getSubTotalAsMoney()
2847
    {
2848
        return EcommerceCurrency::get_money_object_from_order_currency($this->SubTotal(), $this);
2849
    }
2850
2851
    /**
2852
     * @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...
2853
     * @param bool         $stopAtExcludedModifier - when this flag is TRUE, we stop adding the modifiers when we reach an excluded modifier.
2854
     *
2855
     * @return Currency (DB Object)
2856
     **/
2857
    public function ModifiersSubTotalAsCurrencyObject($excluded = null, $stopAtExcludedModifier = false)
2858
    {
2859
        return DBField::create_field('Currency', $this->ModifiersSubTotal($excluded, $stopAtExcludedModifier));
2860
    }
2861
2862
    /**
2863
     * @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...
2864
     * @param bool         $stopAtExcludedModifier - when this flag is TRUE, we stop adding the modifiers when we reach an excluded modifier.
2865
     *
2866
     * @return Money (DB Object)
2867
     **/
2868
    public function ModifiersSubTotalAsMoneyObject($excluded = null, $stopAtExcludedModifier = false)
2869
    {
2870
        return EcommerceCurrency::get_money_object_from_order_currency($this->ModifiersSubTotal($excluded, $stopAtExcludedModifier), $this);
2871
    }
2872
2873
    /**
2874
     * Returns the total cost of an order including the additional charges or deductions of its modifiers.
2875
     *
2876
     * @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...
2877
     */
2878
    public function Total()
2879
    {
2880
        return $this->getTotal();
2881
    }
2882
    public function getTotal()
2883
    {
2884
        return $this->SubTotal() + $this->ModifiersSubTotal();
2885
    }
2886
2887
    /**
2888
     * @return Currency (DB Object)
2889
     **/
2890
    public function TotalAsCurrencyObject()
2891
    {
2892
        return DBField::create_field('Currency', $this->Total());
2893
    }
2894
2895
    /**
2896
     * @return Money
2897
     **/
2898
    public function TotalAsMoney()
2899
    {
2900
        return $this->getTotalAsMoney();
2901
    }
2902
    public function getTotalAsMoney()
2903
    {
2904
        return EcommerceCurrency::get_money_object_from_order_currency($this->Total(), $this);
2905
    }
2906
2907
    /**
2908
     * Checks to see if any payments have been made on this order
2909
     * and if so, subracts the payment amount from the order.
2910
     *
2911
     * @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...
2912
     **/
2913
    public function TotalOutstanding()
2914
    {
2915
        return $this->getTotalOutstanding();
2916
    }
2917
    public function getTotalOutstanding()
2918
    {
2919
        if ($this->IsSubmitted()) {
2920
            $total = $this->Total();
2921
            $paid = $this->TotalPaid();
2922
            $outstanding = $total - $paid;
2923
            $maxDifference = EcommerceConfig::get('Order', 'maximum_ignorable_sales_payments_difference');
2924
            if (abs($outstanding) < $maxDifference) {
2925
                $outstanding = 0;
2926
            }
2927
2928
            return floatval($outstanding);
2929
        } else {
2930
            return 0;
2931
        }
2932
    }
2933
2934
    /**
2935
     * @return Currency (DB Object)
2936
     **/
2937
    public function TotalOutstandingAsCurrencyObject()
2938
    {
2939
        return DBField::create_field('Currency', $this->TotalOutstanding());
2940
    }
2941
2942
    /**
2943
     * @return Money
2944
     **/
2945
    public function TotalOutstandingAsMoney()
2946
    {
2947
        return $this->getTotalOutstandingAsMoney();
2948
    }
2949
    public function getTotalOutstandingAsMoney()
2950
    {
2951
        return EcommerceCurrency::get_money_object_from_order_currency($this->TotalOutstanding(), $this);
2952
    }
2953
2954
    /**
2955
     * @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...
2956
     */
2957
    public function TotalPaid()
2958
    {
2959
        return $this->getTotalPaid();
2960
    }
2961
    public function getTotalPaid()
2962
    {
2963
        $paid = 0;
2964
        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...
2965
            foreach ($payments as $payment) {
2966
                if ($payment->Status == 'Success') {
2967
                    $paid += $payment->Amount->getAmount();
2968
                }
2969
            }
2970
        }
2971
        $reverseExchange = 1;
2972
        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...
2973
            $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...
2974
        }
2975
2976
        return $paid * $reverseExchange;
2977
    }
2978
2979
    /**
2980
     * @return Currency (DB Object)
2981
     **/
2982
    public function TotalPaidAsCurrencyObject()
2983
    {
2984
        return DBField::create_field('Currency', $this->TotalPaid());
2985
    }
2986
2987
    /**
2988
     * @return Money
2989
     **/
2990
    public function TotalPaidAsMoney()
2991
    {
2992
        return $this->getTotalPaidAsMoney();
2993
    }
2994
    public function getTotalPaidAsMoney()
2995
    {
2996
        return EcommerceCurrency::get_money_object_from_order_currency($this->TotalPaid(), $this);
2997
    }
2998
2999
    /**
3000
     * returns the total number of OrderItems (not modifiers).
3001
     * This is meant to run as fast as possible to quickly check
3002
     * if there is anything in the cart.
3003
     *
3004
     * @param bool $recalculate - do we need to recalculate (value is retained during lifetime of Object)
3005
     *
3006
     * @return int
3007
     **/
3008
    public function TotalItems($recalculate = false)
3009
    {
3010
        return $this->getTotalItems($recalculate);
3011
    }
3012
    public function getTotalItems($recalculate = false)
3013
    {
3014
        if ($this->totalItems === null || $recalculate) {
3015
            $this->totalItems = OrderItem::get()
3016
                ->where('"OrderAttribute"."OrderID" = '.$this->ID.' AND "OrderItem"."Quantity" > 0')
3017
                ->count();
3018
        }
3019
3020
        return $this->totalItems;
3021
    }
3022
3023
    /**
3024
     * Little shorthand.
3025
     *
3026
     * @param bool $recalculate
3027
     *
3028
     * @return bool
3029
     **/
3030
    public function MoreThanOneItemInCart($recalculate = false)
3031
    {
3032
        return $this->TotalItems($recalculate) > 1 ? true : false;
3033
    }
3034
3035
    /**
3036
     * returns the total number of OrderItems (not modifiers) times their respectective quantities.
3037
     *
3038
     * @param bool $recalculate - force recalculation
3039
     *
3040
     * @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...
3041
     **/
3042
    public function TotalItemsTimesQuantity($recalculate = false)
3043
    {
3044
        return $this->getTotalItemsTimesQuantity($recalculate);
3045
    }
3046
    public function getTotalItemsTimesQuantity($recalculate = false)
3047
    {
3048
        if ($this->totalItemsTimesQuantity === null || $recalculate) {
3049
            //to do, why do we check if you can edit ????
3050
            $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...
3051
                SELECT SUM("OrderItem"."Quantity")
3052
                FROM "OrderItem"
3053
                    INNER JOIN "OrderAttribute" ON "OrderAttribute"."ID" = "OrderItem"."ID"
3054
                WHERE
3055
                    "OrderAttribute"."OrderID" = '.$this->ID.'
3056
                    AND "OrderItem"."Quantity" > 0'
3057
            )->value();
3058
        }
3059
3060
        return $this->totalItemsTimesQuantity - 0;
3061
    }
3062
3063
    /**
3064
     *
3065
     * @return string (country code)
3066
     **/
3067
    public function Country()
3068
    {
3069
        return $this->getCountry();
3070
    }
3071
3072
    /**
3073
    * Returns the country code for the country that applies to the order.
3074
    * @alias  for getCountry
3075
    *
3076
    * @return string - country code e.g. NZ
3077
     */
3078
    public function getCountry()
3079
    {
3080
        $countryCodes = array(
3081
            'Billing' => '',
3082
            'Shipping' => '',
3083
        );
3084
        $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...
3085
        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...
3086
            $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...
3087
            if ($billingAddress) {
3088
                if ($billingAddress->Country) {
3089
                    $countryCodes['Billing'] = $billingAddress->Country;
3090
                }
3091
            }
3092
        }
3093
        if ($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...
3094
            $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...
3095
            if ($shippingAddress) {
3096
                if ($shippingAddress->ShippingCountry) {
3097
                    $countryCodes['Shipping'] = $shippingAddress->ShippingCountry;
3098
                }
3099
            }
3100
        }
3101
        if (
3102
            (EcommerceConfig::get('OrderAddress', 'use_shipping_address_for_main_region_and_country') && $countryCodes['Shipping'])
3103
            ||
3104
            (!$countryCodes['Billing'] && $countryCodes['Shipping'])
3105
        ) {
3106
            $code = $countryCodes['Shipping'];
3107
        } elseif ($countryCodes['Billing']) {
3108
            $code = $countryCodes['Billing'];
3109
        } else {
3110
            $code = EcommerceCountry::get_country_from_ip();
3111
        }
3112
3113
        return $code;
3114
    }
3115
3116
    /**
3117
     * @alias for getFullNameCountry
3118
     *
3119
     * @return string - country name
3120
     **/
3121
    public function FullNameCountry()
3122
    {
3123
        return $this->getFullNameCountry();
3124
    }
3125
3126
    /**
3127
     * returns name of coutry.
3128
     *
3129
     * @return string - country name
3130
     **/
3131
    public function getFullNameCountry()
3132
    {
3133
        return EcommerceCountry::find_title($this->Country());
3134
    }
3135
3136
    /**
3137
     * @alis for getExpectedCountryName
3138
     * @return string - country name
3139
     **/
3140
    public function ExpectedCountryName()
3141
    {
3142
        return $this->getExpectedCountryName();
3143
    }
3144
3145
    /**
3146
     * returns name of coutry that we expect the customer to have
3147
     * This takes into consideration more than just what has been entered
3148
     * for example, it looks at GEO IP.
3149
     *
3150
     * @todo: why do we dont return a string IF there is only one item.
3151
     *
3152
     * @return string - country name
3153
     **/
3154
    public function getExpectedCountryName()
3155
    {
3156
        return EcommerceCountry::find_title(EcommerceCountry::get_country(false, $this->ID));
3157
    }
3158
3159
    /**
3160
     * return the title of the fixed country (if any).
3161
     *
3162
     * @return string | empty string
3163
     **/
3164
    public function FixedCountry()
3165
    {
3166
        return $this->getFixedCountry();
3167
    }
3168
    public function getFixedCountry()
3169
    {
3170
        $code = EcommerceCountry::get_fixed_country_code();
3171
        if ($code) {
3172
            return EcommerceCountry::find_title($code);
3173
        }
3174
3175
        return '';
3176
    }
3177
3178
    /**
3179
     * Returns the region that applies to the order.
3180
     * we check both billing and shipping, in case one of them is empty.
3181
     *
3182
     * @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...
3183
     **/
3184
    public function Region()
3185
    {
3186
        return $this->getRegion();
3187
    }
3188
    public function getRegion()
3189
    {
3190
        $regionIDs = array(
3191
            'Billing' => 0,
3192
            'Shipping' => 0,
3193
        );
3194
        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...
3195
            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...
3196
                if ($billingAddress->RegionID) {
3197
                    $regionIDs['Billing'] = $billingAddress->RegionID;
3198
                }
3199
            }
3200
        }
3201
        if ($this->CanHaveShippingAddress()) {
3202
            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...
3203
                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...
3204
                    if ($shippingAddress->ShippingRegionID) {
3205
                        $regionIDs['Shipping'] = $shippingAddress->ShippingRegionID;
3206
                    }
3207
                }
3208
            }
3209
        }
3210
        if (count($regionIDs)) {
3211
            //note the double-check with $this->CanHaveShippingAddress() and get_use_....
3212
            if ($this->CanHaveShippingAddress() && EcommerceConfig::get('OrderAddress', 'use_shipping_address_for_main_region_and_country') && $regionIDs['Shipping']) {
3213
                return EcommerceRegion::get()->byID($regionIDs['Shipping']);
3214
            } else {
3215
                return EcommerceRegion::get()->byID($regionIDs['Billing']);
3216
            }
3217
        } else {
3218
            return EcommerceRegion::get()->byID(EcommerceRegion::get_region_from_ip());
3219
        }
3220
    }
3221
3222
    /**
3223
     * Casted variable
3224
     * Currency is not the same as the standard one?
3225
     *
3226
     * @return bool
3227
     **/
3228
    public function HasAlternativeCurrency()
3229
    {
3230
        return $this->getHasAlternativeCurrency();
3231
    }
3232
    public function getHasAlternativeCurrency()
3233
    {
3234
        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...
3235
            if ($currency->IsDefault()) {
0 ignored issues
show
Coding Style introduced by
The if-else statement can be simplified to return !$currency->IsDefault();.
Loading history...
3236
                return false;
3237
            } else {
3238
                return true;
3239
            }
3240
        } else {
3241
            return false;
3242
        }
3243
    }
3244
3245
    /**
3246
     * Makes sure exchange rate is updated and maintained before order is submitted
3247
     * This method is public because it could be called from a shopping Cart Object.
3248
     **/
3249
    public function EnsureCorrectExchangeRate()
3250
    {
3251
        if (!$this->IsSubmitted()) {
3252
            $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...
3253
            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...
3254
                if ($currency->IsDefault()) {
3255
                    $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...
3256
                } else {
3257
                    $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...
3258
                }
3259
            } else {
3260
                $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...
3261
            }
3262
            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...
3263
                $this->write();
3264
            }
3265
        }
3266
    }
3267
3268
    /**
3269
     * speeds up processing by storing the IsSubmitted value
3270
     * we start with -1 to know if it has been requested before.
3271
     *
3272
     * @var bool
3273
     */
3274
    protected $_isSubmittedTempVar = -1;
3275
3276
    /**
3277
     * Casted variable - has the order been submitted?
3278
     * alias
3279
     * @param bool $recalculate
3280
     *
3281
     * @return bool
3282
     **/
3283
    public function IsSubmitted($recalculate = true)
3284
    {
3285
        return $this->getIsSubmitted($recalculate);
3286
    }
3287
3288
    /**
3289
     * Casted variable - has the order been submitted?
3290
     *
3291
     * @param bool $recalculate
3292
     *
3293
     * @return bool
3294
     **/
3295
    public function getIsSubmitted($recalculate = false)
3296
    {
3297
        if ($this->_isSubmittedTempVar === -1 || $recalculate) {
3298
            if ($this->SubmissionLog()) {
3299
                $this->_isSubmittedTempVar = true;
3300
            } else {
3301
                $this->_isSubmittedTempVar = false;
3302
            }
3303
        }
3304
3305
        return $this->_isSubmittedTempVar;
3306
    }
3307
3308
    /**
3309
     *
3310
     *
3311
     * @return bool
3312
     */
3313
    public function IsArchived()
3314
    {
3315
        $lastStep = OrderStep::get()->Last();
3316
        if ($lastStep) {
3317
            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...
3318
                return true;
3319
            }
3320
        }
3321
        return false;
3322
    }
3323
3324
    /**
3325
     * Submission Log for this Order (if any).
3326
     *
3327
     * @return Submission Log (OrderStatusLog_Submitted) | Null
3328
     **/
3329
    public function SubmissionLog()
3330
    {
3331
        $className = EcommerceConfig::get('OrderStatusLog', 'order_status_log_class_used_for_submitting_order');
3332
3333
        return $className::get()
3334
            ->Filter(array('OrderID' => $this->ID))
3335
            ->Last();
3336
    }
3337
3338
    /**
3339
     * Submission Log for this Order (if any).
3340
     *
3341
     * @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...
3342
     **/
3343
    public function OrderDate()
3344
    {
3345
        $object = $this->SubmissionLog();
3346
        if($object) {
3347
            $created = $object->Created;
3348
        } else {
3349
            $created = $this->LastEdited;
3350
        }
3351
3352
        return DBField::create_field('SS_Datetime', $created);
3353
    }
3354
3355
    /**
3356
     * @return int
3357
     */
3358
    public function SecondsSinceBeingSubmitted()
3359
    {
3360
        if ($submissionLog = $this->SubmissionLog()) {
3361
            return time() - strtotime($submissionLog->Created);
3362
        } else {
3363
            return 0;
3364
        }
3365
    }
3366
3367
    /**
3368
     * if the order can not be submitted,
3369
     * then the reasons why it can not be submitted
3370
     * will be returned by this method.
3371
     *
3372
     * @see Order::canSubmit
3373
     *
3374
     * @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...
3375
     */
3376
    public function SubmitErrors()
3377
    {
3378
        $al = null;
3379
        $extendedSubmitErrors = $this->extend('updateSubmitErrors');
3380
        if ($extendedSubmitErrors !== null && is_array($extendedSubmitErrors) && count($extendedSubmitErrors)) {
3381
            $al = ArrayList::create();
3382
            foreach ($extendedSubmitErrors as $returnResultArray) {
3383
                foreach ($returnResultArray as $issue) {
3384
                    if ($issue) {
3385
                        $al->push(ArrayData::create(array("Title" => $issue)));
3386
                    }
3387
                }
3388
            }
3389
        }
3390
        return $al;
3391
    }
3392
3393
    /**
3394
     * Casted variable - has the order been submitted?
3395
     *
3396
     * @param bool $withDetail
3397
     *
3398
     * @return string
3399
     **/
3400
    public function CustomerStatus($withDetail = true)
3401
    {
3402
        return $this->getCustomerStatus($withDetail);
3403
    }
3404
    public function getCustomerStatus($withDetail = true)
3405
    {
3406
        $str = '';
3407
        if ($this->MyStep()->ShowAsUncompletedOrder) {
3408
            $str = _t('Order.UNCOMPLETED', 'Uncompleted');
3409
        } elseif ($this->MyStep()->ShowAsInProcessOrder) {
3410
            $str = _t('Order.IN_PROCESS', 'In Process');
3411
        } elseif ($this->MyStep()->ShowAsCompletedOrder) {
3412
            $str = _t('Order.COMPLETED', 'Completed');
3413
        }
3414
        if ($withDetail) {
3415
            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...
3416
                $str .= ' ('.$this->MyStep()->Name.')';
3417
            }
3418
        }
3419
3420
        return $str;
3421
    }
3422
3423
    /**
3424
     * Casted variable - does the order have a potential shipping address?
3425
     *
3426
     * @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...
3427
     **/
3428
    public function CanHaveShippingAddress()
3429
    {
3430
        return $this->getCanHaveShippingAddress();
3431
    }
3432
    public function getCanHaveShippingAddress()
3433
    {
3434
        return EcommerceConfig::get('OrderAddress', 'use_separate_shipping_address');
3435
    }
3436
3437
    /**
3438
     * returns the link to view the Order
3439
     * WHY NOT CHECKOUT PAGE: first we check for cart page.
3440
     *
3441
     * @return CartPage | Null
3442
     */
3443
    public function DisplayPage()
3444
    {
3445
        if ($this->MyStep() && $this->MyStep()->AlternativeDisplayPage()) {
3446
            $page = $this->MyStep()->AlternativeDisplayPage();
3447
        } elseif ($this->IsSubmitted()) {
3448
            $page = OrderConfirmationPage::get()->First();
3449
        } else {
3450
            $page = CartPage::get()
3451
                ->Filter(array('ClassName' => 'CartPage'))
3452
                ->First();
3453
            if (!$page) {
3454
                $page = CheckoutPage::get()->First();
3455
            }
3456
        }
3457
3458
        return $page;
3459
    }
3460
3461
    /**
3462
     * returns the link to view the Order
3463
     * WHY NOT CHECKOUT PAGE: first we check for cart page.
3464
     * If a cart page has been created then we refer through to Cart Page.
3465
     * Otherwise it will default to the checkout page.
3466
     *
3467
     * @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...
3468
     *
3469
     * @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...
3470
     */
3471
    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...
3472
    {
3473
        $page = $this->DisplayPage();
3474
        if ($page) {
3475
            return $page->getOrderLink($this->ID, $action);
3476
        } else {
3477
            user_error('A Cart / Checkout Page + an Order Confirmation Page needs to be setup for the e-commerce module to work.', E_USER_NOTICE);
3478
            $page = ErrorPage::get()
3479
                ->Filter(array('ErrorCode' => '404'))
3480
                ->First();
3481
            if ($page) {
3482
                return $page->Link();
3483
            }
3484
        }
3485
    }
3486
3487
    /**
3488
     * Returns to link to access the Order's API.
3489
     *
3490
     * @param string $version
3491
     * @param string $extension
3492
     *
3493
     * @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...
3494
     */
3495
    public function APILink($version = 'v1', $extension = 'xml')
3496
    {
3497
        return Director::AbsoluteURL("/api/ecommerce/$version/Order/".$this->ID."/.$extension");
3498
    }
3499
3500
    /**
3501
     * returns the link to finalise the Order.
3502
     *
3503
     * @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...
3504
     */
3505
    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...
3506
    {
3507
        $page = CheckoutPage::get()->First();
3508
        if ($page) {
3509
            return $page->Link();
3510
        } else {
3511
            $page = ErrorPage::get()
3512
                ->Filter(array('ErrorCode' => '404'))
3513
                ->First();
3514
            if ($page) {
3515
                return $page->Link();
3516
            }
3517
        }
3518
    }
3519
3520
    /**
3521
     * Converts the Order into HTML, based on the Order Template.
3522
     *
3523
     * @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...
3524
     **/
3525
    public function ConvertToHTML()
3526
    {
3527
        Config::nest();
3528
        Config::inst()->update('SSViewer', 'theme_enabled', true);
3529
        $html = $this->renderWith('Order');
3530
        Config::unnest();
3531
        $html = preg_replace('/(\s)+/', ' ', $html);
3532
3533
        return DBField::create_field('HTMLText', $html);
3534
    }
3535
3536
    /**
3537
     * Converts the Order into a serialized string
3538
     * TO DO: check if this works and check if we need to use special sapphire serialization code.
3539
     *
3540
     * @return string - serialized object
3541
     **/
3542
    public function ConvertToString()
3543
    {
3544
        return serialize($this->addHasOneAndHasManyAsVariables());
3545
    }
3546
3547
    /**
3548
     * Converts the Order into a JSON object
3549
     * TO DO: check if this works and check if we need to use special sapphire JSON code.
3550
     *
3551
     * @return string -  JSON
3552
     **/
3553
    public function ConvertToJSON()
3554
    {
3555
        return json_encode($this->addHasOneAndHasManyAsVariables());
3556
    }
3557
3558
    /**
3559
     * returns itself wtih more data added as variables.
3560
     * We add has_one and has_many as variables like this: $this->MyHasOne_serialized = serialize($this->MyHasOne()).
3561
     *
3562
     * @return Order - with most important has one and has many items included as variables.
3563
     **/
3564
    protected function addHasOneAndHasManyAsVariables()
3565
    {
3566
        $object = clone $this;
3567
        $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...
3568
        $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...
3569
        $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...
3570
        $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...
3571
        $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...
3572
        $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...
3573
        $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...
3574
3575
        return $object;
3576
    }
3577
3578
/*******************************************************
3579
   * 9. TEMPLATE RELATED STUFF
3580
*******************************************************/
3581
3582
    /**
3583
     * returns the instance of EcommerceConfigAjax for use in templates.
3584
     * In templates, it is used like this:
3585
     * $EcommerceConfigAjax.TableID.
3586
     *
3587
     * @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...
3588
     **/
3589
    public function AJAXDefinitions()
3590
    {
3591
        return EcommerceConfigAjax::get_one($this);
3592
    }
3593
3594
    /**
3595
     * returns the instance of EcommerceDBConfig.
3596
     *
3597
     * @return EcommerceDBConfig
3598
     **/
3599
    public function EcomConfig()
3600
    {
3601
        return EcommerceDBConfig::current_ecommerce_db_config();
3602
    }
3603
3604
    /**
3605
     * Collects the JSON data for an ajax return of the cart.
3606
     *
3607
     * @param array $js
3608
     *
3609
     * @return array (for use in AJAX for JSON)
3610
     **/
3611
    public function updateForAjax(array $js)
3612
    {
3613
        $function = EcommerceConfig::get('Order', 'ajax_subtotal_format');
3614
        if (is_array($function)) {
3615
            list($function, $format) = $function;
3616
        }
3617
        $subTotal = $this->$function();
3618
        if (isset($format)) {
3619
            $subTotal = $subTotal->$format();
3620
            unset($format);
3621
        }
3622
        $function = EcommerceConfig::get('Order', 'ajax_total_format');
3623
        if (is_array($function)) {
3624
            list($function, $format) = $function;
3625
        }
3626
        $total = $this->$function();
3627
        if (isset($format)) {
3628
            $total = $total->$format();
3629
        }
3630
        $ajaxObject = $this->AJAXDefinitions();
3631
        $js[] = array(
3632
            't' => 'id',
3633
            's' => $ajaxObject->TableSubTotalID(),
3634
            'p' => 'innerHTML',
3635
            'v' => $subTotal,
3636
        );
3637
        $js[] = array(
3638
            't' => 'id',
3639
            's' => $ajaxObject->TableTotalID(),
3640
            'p' => 'innerHTML',
3641
            'v' => $total,
3642
        );
3643
        $js[] = array(
3644
            't' => 'class',
3645
            's' => $ajaxObject->TotalItemsClassName(),
3646
            'p' => 'innerHTML',
3647
            'v' => $this->TotalItems($recalculate = true),
3648
        );
3649
        $js[] = array(
3650
            't' => 'class',
3651
            's' => $ajaxObject->TotalItemsTimesQuantityClassName(),
3652
            'p' => 'innerHTML',
3653
            'v' => $this->TotalItemsTimesQuantity(),
3654
        );
3655
        $js[] = array(
3656
            't' => 'class',
3657
            's' => $ajaxObject->ExpectedCountryClassName(),
3658
            'p' => 'innerHTML',
3659
            'v' => $this->ExpectedCountryName(),
3660
        );
3661
3662
        return $js;
3663
    }
3664
3665
    /**
3666
     * @ToDO: move to more appropriate class
3667
     *
3668
     * @return float
3669
     **/
3670
    public function SubTotalCartValue()
3671
    {
3672
        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...
3673
    }
3674
3675
/*******************************************************
3676
   * 10. STANDARD SS METHODS (requireDefaultRecords, onBeforeDelete, etc...)
3677
*******************************************************/
3678
3679
    /**
3680
     *standard SS method.
3681
     **/
3682
    public function populateDefaults()
3683
    {
3684
        parent::populateDefaults();
3685
    }
3686
3687
    public function onBeforeWrite()
3688
    {
3689
        parent::onBeforeWrite();
3690
        if (! $this->getCanHaveShippingAddress()) {
3691
            $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...
3692
        }
3693
        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...
3694
            $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...
3695
        }
3696
        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...
3697
            $generator = Injector::inst()->create('RandomGenerator');
3698
            $token = $generator->randomToken('sha1');
3699
            $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...
3700
        }
3701
    }
3702
3703
    /**
3704
     * standard SS method
3705
     * adds the ability to update order after writing it.
3706
     **/
3707
    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...
3708
    {
3709
        parent::onAfterWrite();
3710
        //crucial!
3711
        self::set_needs_recalculating(true, $this->ID);
3712
        // quick double-check
3713
        if ($this->IsCancelled() && ! $this->IsArchived()) {
3714
            $this->Archive($avoidWrites = true);
3715
        }
3716
        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...
3717
            //do nothing
3718
        } else {
3719
            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...
3720
                $this->calculateOrderAttributes($recalculate = false);
3721
                if (EcommerceRole::current_member_is_shop_admin()) {
3722
                    if (isset($_REQUEST['SubmitOrderViaCMS'])) {
3723
                        $this->tryToFinaliseOrder();
3724
                        //just in case it writes again...
3725
                        unset($_REQUEST['SubmitOrderViaCMS']);
3726
                    }
3727
                }
3728
            }
3729
        }
3730
    }
3731
3732
    /**
3733
     *standard SS method.
3734
     *
3735
     * delete attributes, statuslogs, and payments
3736
     * THIS SHOULD NOT BE USED AS ORDERS SHOULD BE CANCELLED NOT DELETED
3737
     */
3738
    public function onBeforeDelete()
3739
    {
3740
        parent::onBeforeDelete();
3741
        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...
3742
            foreach ($attributes as $attribute) {
3743
                $attribute->delete();
3744
                $attribute->destroy();
3745
            }
3746
        }
3747
3748
        //THE REST WAS GIVING ERRORS - POSSIBLY DUE TO THE FUNNY RELATIONSHIP (one-one, two times...)
3749
        /*
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...
3750
        if($billingAddress = $this->BillingAddress()) {
3751
            if($billingAddress->exists()) {
3752
                $billingAddress->delete();
3753
                $billingAddress->destroy();
3754
            }
3755
        }
3756
        if($shippingAddress = $this->ShippingAddress()) {
3757
            if($shippingAddress->exists()) {
3758
                $shippingAddress->delete();
3759
                $shippingAddress->destroy();
3760
            }
3761
        }
3762
3763
        if($statuslogs = $this->OrderStatusLogs()){
3764
            foreach($statuslogs as $log){
3765
                $log->delete();
3766
                $log->destroy();
3767
            }
3768
        }
3769
        if($payments = $this->Payments()){
3770
            foreach($payments as $payment){
3771
                $payment->delete();
3772
                $payment->destroy();
3773
            }
3774
        }
3775
        if($emails = $this->Emails()) {
3776
            foreach($emails as $email){
3777
                $email->delete();
3778
                $email->destroy();
3779
            }
3780
        }
3781
        */
3782
    }
3783
3784
/*******************************************************
3785
   * 11. DEBUG
3786
*******************************************************/
3787
3788
    /**
3789
     * Debug helper method.
3790
     * Can be called from /shoppingcart/debug/.
3791
     *
3792
     * @return string
3793
     */
3794
    public function debug()
3795
    {
3796
        $this->calculateOrderAttributes(true);
3797
3798
        return EcommerceTaskDebugCart::debug_object($this);
3799
    }
3800
}
3801