Completed
Push — master ( 0b97e5...c39ce1 )
by Nicolaas
04:16
created

Order::getIsPaidNice()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 2
nc 2
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 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' => 'Varchar',
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 = DataObject::get_one('OrderStep');
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 = DataObject::get_one('OrderStep');
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
     * @return null
1126
     **/
1127
    public function tryToFinaliseOrder($runAgain = false, $fromOrderQueue = false)
1128
    {
1129
        if (empty(self::$_try_to_finalise_order_is_running[$this->ID]) || $runAgain) {
1130
            self::$_try_to_finalise_order_is_running[$this->ID] = true;
1131
1132
            //if the order has been cancelled then we do not process it ...
1133
            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...
1134
                $this->Archive(true);
1135
1136
                return;
1137
            }
1138
            // if it is in the queue it has to run from the queue tasks
1139
            // if it ruins from the queue tasks then it has to be one currently processing.
1140
            $queueObjectSingleton = Injector::inst()->get('OrderProcessQueue');
1141
            if ($myQueueObject = $queueObjectSingleton->getQueueObject($this)) {
1142
                if($fromOrderQueue) {
1143
                    if ( ! $myQueueObject->InProcess) {
1144
1145
                        return;
1146
                    }
1147
                } else {
1148
1149
                    return;
1150
                }
1151
            }
1152
            //a little hack to make sure we do not rely on a stored value
1153
            //of "isSubmitted"
1154
            $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...
1155
            //status of order is being progressed
1156
            $nextStatusID = $this->doNextStatus();
1157
            if ($nextStatusID) {
1158
                $nextStatusObject = OrderStep::get()->byID($nextStatusID);
1159
                if ($nextStatusObject) {
1160
                    $delay = $nextStatusObject->CalculatedDeferTimeInSeconds($this);
1161
                    if ($delay > 0) {
1162
                        //adjust delay time from seconds since being submitted
1163
                        if ($nextStatusObject->DeferFromSubmitTime) {
1164
                            $delay = $delay - $this->SecondsSinceBeingSubmitted();
1165
                            if ($delay < 0) {
1166
                                $delay = 0;
1167
                            }
1168
                        }
1169
                        $queueObjectSingleton->AddOrderToQueue(
1170
                            $this,
1171
                            $delay
1172
                        );
1173
                    } else {
1174
                        //status has been completed, so it can be released
1175
                        self::$_try_to_finalise_order_is_running[$this->ID] = false;
1176
                        $this->tryToFinaliseOrder($runAgain, $fromOrderQueue);
1177
                    }
1178
                }
1179
            }
1180
        }
1181
    }
1182
1183
    /**
1184
     * Goes through the order steps and tries to "apply" the next step
1185
     * Step is updated after the other one is completed...
1186
     *
1187
     * @return int (StatusID or false if the next status can not be "applied")
1188
     **/
1189
    public function doNextStatus()
1190
    {
1191
        if ($this->MyStep()->initStep($this)) {
1192
            if ($this->MyStep()->doStep($this)) {
1193
                if ($nextOrderStepObject = $this->MyStep()->nextStep($this)) {
1194
                    $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...
1195
                    $this->write();
1196
1197
                    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...
1198
                }
1199
            }
1200
        }
1201
1202
        return 0;
1203
    }
1204
1205
    /**
1206
     * cancel an order.
1207
     *
1208
     * @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...
1209
     * @param string $reason - (optional) the reason the order is cancelled
1210
     *
1211
     * @return OrderStatusLog_Cancel
1212
     */
1213
    public function Cancel($member = null, $reason = '')
1214
    {
1215
        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...
1216
            //we have a valid member
1217
        } else {
1218
            $member = EcommerceRole::get_default_shop_admin_user();
1219
        }
1220
        if($member) {
1221
            //archive and write
1222
            $this->Archive($avoidWrites = true);
1223
            if($avoidWrites) {
1224
                DB::query('Update "Order" SET CancelledByID = '.$member->ID.' WHERE ID = '.$this->ID.' LIMIT 1;');
1225
            } else {
1226
                $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...
1227
                $this->write();
1228
            }
1229
            //create log ...
1230
            $log = OrderStatusLog_Cancel::create();
1231
            $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...
1232
            $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...
1233
            $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...
1234
            if ($member->IsShopAdmin()) {
1235
                $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...
1236
            }
1237
            $log->write();
1238
            //remove from queue ...
1239
            $queueObjectSingleton = Injector::inst()->get('OrderProcessQueue');
1240
            $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...
1241
            $this->extend('doCancel', $member, $log);
1242
1243
            return $log;
1244
        }
1245
    }
1246
1247
    /**
1248
     * returns true if successful.
1249
     *
1250
     * @param bool $avoidWrites
1251
     *
1252
     * @return bool
1253
     */
1254
    public function Archive($avoidWrites = true)
1255
    {
1256
        $lastOrderStep = OrderStep::get()->Last();
1257
        if ($lastOrderStep) {
1258
            if ($avoidWrites) {
1259
                DB::query('
1260
                    UPDATE "Order"
1261
                    SET "Order"."StatusID" = '.$lastOrderStep->ID.'
1262
                    WHERE "Order"."ID" = '.$this->ID.'
1263
                    LIMIT 1
1264
                ');
1265
1266
                return true;
1267
            } else {
1268
                $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...
1269
                $this->write();
1270
1271
                return true;
1272
            }
1273
        }
1274
1275
        return false;
1276
    }
1277
1278
/*******************************************************
1279
   * 3. STATUS RELATED FUNCTIONS / SHORTCUTS
1280
*******************************************************/
1281
1282
    /**
1283
     * Avoids caching of $this->Status().
1284
     *
1285
     * @return DataObject (current OrderStep)
1286
     */
1287
    public function MyStep()
1288
    {
1289
        $step = null;
1290
        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...
1291
            $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...
1292
        }
1293
        if (! $step) {
1294
            $step = DataObject::get_one(
1295
                'OrderStep',
1296
                null,
1297
                $cacheDataObjectGetOne = false
1298
            );
1299
        }
1300
        if (! $step) {
1301
            $step = OrderStep_Created::create();
1302
        }
1303
        if (! $step) {
1304
            user_error('You need an order step in your Database.');
1305
        }
1306
1307
        return $step;
1308
    }
1309
1310
    /**
1311
     * Return the OrderStatusLog that is relevant to the Order status.
1312
     *
1313
     * @return OrderStatusLog
1314
     */
1315
    public function RelevantLogEntry()
1316
    {
1317
        return $this->MyStep()->RelevantLogEntry($this);
1318
    }
1319
1320
    /**
1321
     * @return OrderStep (current OrderStep that can be seen by customer)
1322
     */
1323
    public function CurrentStepVisibleToCustomer()
1324
    {
1325
        $obj = $this->MyStep();
1326
        if ($obj->HideStepFromCustomer) {
1327
            $obj = OrderStep::get()->where('"OrderStep"."Sort" < '.$obj->Sort.' AND "HideStepFromCustomer" = 0')->Last();
1328
            if (!$obj) {
1329
                $obj = DataObject::get_one('OrderStep');
1330
            }
1331
        }
1332
1333
        return $obj;
1334
    }
1335
1336
    /**
1337
     * works out if the order is still at the first OrderStep.
1338
     *
1339
     * @return bool
1340
     */
1341
    public function IsFirstStep()
1342
    {
1343
        $firstStep = DataObject::get_one('OrderStep');
1344
        $currentStep = $this->MyStep();
1345
        if ($firstStep && $currentStep) {
1346
            if ($firstStep->ID == $currentStep->ID) {
1347
                return true;
1348
            }
1349
        }
1350
1351
        return false;
1352
    }
1353
1354
    /**
1355
     * Is the order still being "edited" by the customer?
1356
     *
1357
     * @return bool
1358
     */
1359
    public function IsInCart()
1360
    {
1361
        return (bool) $this->IsSubmitted() ? false : true;
1362
    }
1363
1364
    /**
1365
     * The order has "passed" the IsInCart phase.
1366
     *
1367
     * @return bool
1368
     */
1369
    public function IsPastCart()
1370
    {
1371
        return (bool) $this->IsInCart() ? false : true;
1372
    }
1373
1374
    /**
1375
     * Are there still steps the order needs to go through?
1376
     *
1377
     * @return bool
1378
     */
1379
    public function IsUncomplete()
1380
    {
1381
        return (bool) $this->MyStep()->ShowAsUncompletedOrder;
1382
    }
1383
1384
    /**
1385
     * Is the order in the :"processing" phaase.?
1386
     *
1387
     * @return bool
1388
     */
1389
    public function IsProcessing()
1390
    {
1391
        return (bool) $this->MyStep()->ShowAsInProcessOrder;
1392
    }
1393
1394
    /**
1395
     * Is the order completed?
1396
     *
1397
     * @return bool
1398
     */
1399
    public function IsCompleted()
1400
    {
1401
        return (bool) $this->MyStep()->ShowAsCompletedOrder;
1402
    }
1403
1404
    /**
1405
     * Has the order been paid?
1406
     * TODO: why do we check if there is a total at all?
1407
     *
1408
     * @return bool
1409
     */
1410
    public function IsPaid()
1411
    {
1412
        if ($this->IsSubmitted()) {
1413
            return (bool) (($this->Total() >= 0) && ($this->TotalOutstanding() <= 0));
1414
        }
1415
1416
        return false;
1417
    }
1418
1419
    /**
1420
     * @alias for getIsPaidNice
1421
     * @return string
1422
     */
1423
    public function IsPaidNice()
1424
    {
1425
        return $this->getIsPaidNice();
1426
    }
1427
1428
1429
    public function getIsPaidNice()
1430
    {
1431
        return $this->IsPaid() ? 'yes' : 'no';
1432
    }
1433
1434
1435
    /**
1436
     * Has the order been paid?
1437
     * TODO: why do we check if there is a total at all?
1438
     *
1439
     * @return bool
1440
     */
1441
    public function PaymentIsPending()
1442
    {
1443
        if ($this->IsSubmitted()) {
1444
            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...
1445
                //do nothing;
1446
            } 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...
1447
                foreach ($payments as $payment) {
1448
                    if ('Pending' == $payment->Status) {
1449
                        return true;
1450
                    }
1451
                }
1452
            }
1453
        }
1454
1455
        return false;
1456
    }
1457
1458
    /**
1459
     * shows payments that are meaningfull
1460
     * if the order has been paid then only show successful payments.
1461
     *
1462
     * @return DataList
1463
     */
1464
    public function RelevantPayments()
1465
    {
1466
        if ($this->IsPaid()) {
1467
            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...
1468
            //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...
1469
            //	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...
1470
        } else {
1471
            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...
1472
        }
1473
    }
1474
1475
    /**
1476
     * Has the order been cancelled?
1477
     *
1478
     * @return bool
1479
     */
1480
    public function IsCancelled()
1481
    {
1482
        return $this->getIsCancelled();
1483
    }
1484
    public function getIsCancelled()
1485
    {
1486
        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...
1487
    }
1488
1489
    /**
1490
     * Has the order been cancelled by the customer?
1491
     *
1492
     * @return bool
1493
     */
1494
    public function IsCustomerCancelled()
1495
    {
1496
        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...
1497
            return true;
1498
        }
1499
1500
        return false;
1501
    }
1502
1503
    /**
1504
     * Has the order been cancelled by the  administrator?
1505
     *
1506
     * @return bool
1507
     */
1508
    public function IsAdminCancelled()
1509
    {
1510
        if ($this->IsCancelled()) {
1511
            if (!$this->IsCustomerCancelled()) {
1512
                $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...
1513
                if ($admin) {
1514
                    if ($admin->IsShopAdmin()) {
1515
                        return true;
1516
                    }
1517
                }
1518
            }
1519
        }
1520
1521
        return false;
1522
    }
1523
1524
    /**
1525
     * Is the Shop Closed for business?
1526
     *
1527
     * @return bool
1528
     */
1529
    public function ShopClosed()
1530
    {
1531
        return EcomConfig()->ShopClosed;
1532
    }
1533
1534
/*******************************************************
1535
   * 4. LINKING ORDER WITH MEMBER AND ADDRESS
1536
*******************************************************/
1537
1538
    /**
1539
     * Returns a member linked to the order.
1540
     * If a member is already linked, it will return the existing member.
1541
     * Otherwise it will return a new Member.
1542
     *
1543
     * Any new member is NOT written, because we dont want to create a new member unless we have to!
1544
     * We will not add a member to the order unless a new one is created in the checkout
1545
     * OR the member is logged in / logs in.
1546
     *
1547
     * Also note that if a new member is created, it is not automatically written
1548
     *
1549
     * @param bool $forceCreation - if set to true then the member will always be saved in the database.
1550
     *
1551
     * @return Member
1552
     **/
1553
    public function CreateOrReturnExistingMember($forceCreation = false)
1554
    {
1555
        if ($this->IsSubmitted()) {
1556
            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...
1557
        }
1558
        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...
1559
            $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...
1560
        } elseif ($member = Member::currentUser()) {
1561
            if (!$member->IsShopAdmin()) {
1562
                $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...
1563
                $this->write();
1564
            }
1565
        }
1566
        $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...
1567
        if (!$member) {
1568
            $member = new Member();
1569
        }
1570
        if ($member && $forceCreation) {
1571
            $member->write();
1572
        }
1573
1574
        return $member;
1575
    }
1576
1577
    /**
1578
     * Returns either the existing one or a new Order Address...
1579
     * All Orders will have a Shipping and Billing address attached to it.
1580
     * Method used to retrieve object e.g. for $order->BillingAddress(); "BillingAddress" is the method name you can use.
1581
     * If the method name is the same as the class name then dont worry about providing one.
1582
     *
1583
     * @param string $className             - ClassName of the Address (e.g. BillingAddress or ShippingAddress)
1584
     * @param string $alternativeMethodName - method to retrieve Address
1585
     **/
1586
    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...
1587
    {
1588
        if ($this->exists()) {
1589
            $methodName = $className;
1590
            if ($alternativeMethodName) {
1591
                $methodName = $alternativeMethodName;
1592
            }
1593
            if ($this->IsSubmitted()) {
1594
                return $this->$methodName();
1595
            }
1596
            $variableName = $className.'ID';
1597
            $address = null;
1598
            if ($this->$variableName) {
1599
                $address = $this->$methodName();
1600
            }
1601
            if (!$address) {
1602
                $address = new $className();
1603
                if ($member = $this->CreateOrReturnExistingMember()) {
1604
                    if ($member->exists()) {
1605
                        $address->FillWithLastAddressFromMember($member, $write = false);
1606
                    }
1607
                }
1608
            }
1609
            if ($address) {
1610
                if (!$address->exists()) {
1611
                    $address->write();
1612
                }
1613
                if ($address->OrderID != $this->ID) {
1614
                    $address->OrderID = $this->ID;
1615
                    $address->write();
1616
                }
1617
                if ($this->$variableName != $address->ID) {
1618
                    if (!$this->IsSubmitted()) {
1619
                        $this->$variableName = $address->ID;
1620
                        $this->write();
1621
                    }
1622
                }
1623
1624
                return $address;
1625
            }
1626
        }
1627
1628
        return;
1629
    }
1630
1631
    /**
1632
     * Sets the country in the billing and shipping address.
1633
     *
1634
     * @param string $countryCode            - code for the country e.g. NZ
1635
     * @param bool   $includeBillingAddress
1636
     * @param bool   $includeShippingAddress
1637
     **/
1638
    public function SetCountryFields($countryCode, $includeBillingAddress = true, $includeShippingAddress = true)
1639
    {
1640
        if ($this->IsSubmitted()) {
1641
            user_error('Can not change country in submitted order', E_USER_NOTICE);
1642
        } else {
1643
            if ($includeBillingAddress) {
1644
                if ($billingAddress = $this->CreateOrReturnExistingAddress('BillingAddress')) {
1645
                    $billingAddress->SetCountryFields($countryCode);
1646
                }
1647
            }
1648
            if (EcommerceConfig::get('OrderAddress', 'use_separate_shipping_address')) {
1649
                if ($includeShippingAddress) {
1650
                    if ($shippingAddress = $this->CreateOrReturnExistingAddress('ShippingAddress')) {
1651
                        $shippingAddress->SetCountryFields($countryCode);
1652
                    }
1653
                }
1654
            }
1655
        }
1656
    }
1657
1658
    /**
1659
     * Sets the region in the billing and shipping address.
1660
     *
1661
     * @param int $regionID - ID for the region to be set
1662
     **/
1663
    public function SetRegionFields($regionID)
1664
    {
1665
        if ($this->IsSubmitted()) {
1666
            user_error('Can not change country in submitted order', E_USER_NOTICE);
1667
        } else {
1668
            if ($billingAddress = $this->CreateOrReturnExistingAddress('BillingAddress')) {
1669
                $billingAddress->SetRegionFields($regionID);
1670
            }
1671
            if ($this->CanHaveShippingAddress()) {
1672
                if ($shippingAddress = $this->CreateOrReturnExistingAddress('ShippingAddress')) {
1673
                    $shippingAddress->SetRegionFields($regionID);
1674
                }
1675
            }
1676
        }
1677
    }
1678
1679
    /**
1680
     * Stores the preferred currency of the order.
1681
     * IMPORTANTLY we store the exchange rate for future reference...
1682
     *
1683
     * @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...
1684
     */
1685
    public function UpdateCurrency($newCurrency)
1686
    {
1687
        if ($this->IsSubmitted()) {
1688
            user_error('Can not set the currency after the order has been submitted', E_USER_NOTICE);
1689
        } else {
1690
            if (! is_a($newCurrency, Object::getCustomClass('EcommerceCurrency'))) {
1691
                $newCurrency = EcommerceCurrency::default_currency();
1692
            }
1693
            $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...
1694
            $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...
1695
            $this->write();
1696
        }
1697
    }
1698
1699
    /**
1700
     * alias for UpdateCurrency.
1701
     *
1702
     * @param EcommerceCurrency $currency
1703
     */
1704
    public function SetCurrency($currency)
1705
    {
1706
        $this->UpdateCurrency($currency);
1707
    }
1708
1709
/*******************************************************
1710
   * 5. CUSTOMER COMMUNICATION
1711
*******************************************************/
1712
1713
    /**
1714
     * Send the invoice of the order by email.
1715
     *
1716
     * @param string $emailClassName     (optional) class used to send email
1717
     * @param string $subject            (optional) subject for the email
1718
     * @param string $message            (optional) the main message in the email
1719
     * @param bool   $resend             (optional) send the email even if it has been sent before
1720
     * @param bool   $adminOnlyOrToEmail (optional) sends the email to the ADMIN ONLY, if you provide an email, it will go to the email...
1721
     *
1722
     * @return bool TRUE on success, FALSE on failure
1723
     */
1724
    public function sendEmail(
1725
        $emailClassName = 'Order_InvoiceEmail',
1726
        $subject = '',
1727
        $message = '',
1728
        $resend = false,
1729
        $adminOnlyOrToEmail = false
1730
    ) {
1731
        return $this->prepareAndSendEmail(
1732
            $emailClassName,
1733
            $subject,
1734
            $message,
1735
            $resend,
1736
            $adminOnlyOrToEmail
1737
        );
1738
    }
1739
1740
    /**
1741
     * Sends a message to the shop admin ONLY and not to the customer
1742
     * This can be used by ordersteps and orderlogs to notify the admin of any potential problems.
1743
     *
1744
     * @param string         $emailClassName       - (optional) template to be used ...
1745
     * @param string         $subject              - (optional) subject for the email
1746
     * @param string         $message              - (optional) message to be added with the email
1747
     * @param bool           $resend               - (optional) can it be sent twice?
1748
     * @param bool | string  $adminOnlyOrToEmail   - (optional) sends the email to the ADMIN ONLY, if you provide an email, it will go to the email...
1749
     *
1750
     * @return bool TRUE for success, FALSE for failure (not tested)
1751
     */
1752
    public function sendAdminNotification(
1753
        $emailClassName = 'Order_ErrorEmail',
1754
        $subject = '',
1755
        $message = '',
1756
        $resend = false,
1757
        $adminOnlyOrToEmail = true
1758
    ) {
1759
        return $this->prepareAndSendEmail(
1760
            $emailClassName,
1761
            $subject,
1762
            $message,
1763
            $resend,
1764
            $adminOnlyOrToEmail
1765
        );
1766
    }
1767
1768
    /**
1769
     * returns the order formatted as an email.
1770
     *
1771
     * @param string $emailClassName - template to use.
1772
     * @param string $subject        - (optional) the subject (which can be used as title in email)
1773
     * @param string $message        - (optional) the additional message
1774
     *
1775
     * @return string (html)
1776
     */
1777
    public function renderOrderInEmailFormat(
1778
        $emailClassName,
1779
        $subject = '',
1780
        $message = ''
1781
    )
1782
    {
1783
        $arrayData = $this->createReplacementArrayForEmail($subject, $message);
1784
        Config::nest();
1785
        Config::inst()->update('SSViewer', 'theme_enabled', true);
1786
        $html = $arrayData->renderWith($emailClassName);
1787
        Config::unnest();
1788
1789
        return Order_Email::emogrify_html($html);
1790
    }
1791
1792
    /**
1793
     * Send a mail of the order to the client (and another to the admin).
1794
     *
1795
     * @param string         $emailClassName       - (optional) template to be used ...
1796
     * @param string         $subject              - (optional) subject for the email
1797
     * @param string         $message              - (optional) message to be added with the email
1798
     * @param bool           $resend               - (optional) can it be sent twice?
1799
     * @param bool | string  $adminOnlyOrToEmail   - (optional) sends the email to the ADMIN ONLY, if you provide an email, it will go to the email...
1800
     *
1801
     * @return bool TRUE for success, FALSE for failure (not tested)
1802
     */
1803
    protected function prepareAndSendEmail(
1804
        $emailClassName,
1805
        $subject,
1806
        $message,
1807
        $resend = false,
1808
        $adminOnlyOrToEmail = false
1809
    ) {
1810
        $arrayData = $this->createReplacementArrayForEmail($subject, $message);
1811
        $from = Order_Email::get_from_email();
1812
        //why are we using this email and NOT the member.EMAIL?
1813
        //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...
1814
        if ($adminOnlyOrToEmail) {
1815
            if (filter_var($adminOnlyOrToEmail, FILTER_VALIDATE_EMAIL)) {
1816
                $to = $adminOnlyOrToEmail;
1817
                // invalid e-mail address
1818
            } else {
1819
                $to = Order_Email::get_from_email();
1820
            }
1821
        } else {
1822
            $to = $this->getOrderEmail();
1823
        }
1824
        if ($from && $to) {
1825
            $email = new $emailClassName();
1826
            if (!(is_a($email, Object::getCustomClass('Email')))) {
1827
                user_error('No correct email class provided.', E_USER_ERROR);
1828
            }
1829
            $email->setFrom($from);
1830
            $email->setTo($to);
1831
            //we take the subject from the Array Data, just in case it has been adjusted.
1832
            $email->setSubject($arrayData->getField('Subject'));
1833
            //we also see if a CC and a BCC have been added
1834
            ;
1835
            if ($cc = $arrayData->getField('CC')) {
1836
                $email->setCc($cc);
1837
            }
1838
            if ($bcc = $arrayData->getField('BCC')) {
1839
                $email->setBcc($bcc);
1840
            }
1841
            $email->populateTemplate($arrayData);
1842
            // This might be called from within the CMS,
1843
            // so we need to restore the theme, just in case
1844
            // templates within the theme exist
1845
            Config::nest();
1846
            Config::inst()->update('SSViewer', 'theme_enabled', true);
1847
            $email->setOrder($this);
1848
            $email->setResend($resend);
1849
            $result = $email->send(null);
1850
            Config::unnest();
1851
            if (Director::isDev()) {
1852
                return true;
1853
            } else {
1854
                return $result;
1855
            }
1856
        }
1857
1858
        return false;
1859
    }
1860
1861
    /**
1862
     * returns the Data that can be used in the body of an order Email
1863
     * we add the subject here so that the subject, for example, can be added to the <title>
1864
     * of the email template.
1865
     * we add the subject here so that the subject, for example, can be added to the <title>
1866
     * of the email template.
1867
     *
1868
     * @param string $subject  - (optional) subject for email
1869
     * @param string $message  - (optional) the additional message
1870
     *
1871
     * @return ArrayData
1872
     *                   - Subject - EmailSubject
1873
     *                   - Message - specific message for this order
1874
     *                   - Message - custom message
1875
     *                   - OrderStepMessage - generic message for step
1876
     *                   - Order
1877
     *                   - EmailLogo
1878
     *                   - ShopPhysicalAddress
1879
     *                   - CurrentDateAndTime
1880
     *                   - BaseURL
1881
     *                   - CC
1882
     *                   - BCC
1883
     */
1884
    protected function createReplacementArrayForEmail($subject = '', $message = '')
1885
    {
1886
        $step = $this->MyStep();
1887
        $config = $this->EcomConfig();
1888
        $replacementArray = array();
1889
        //set subject
1890
        if ( ! $subject) {
1891
            $subject = $step->EmailSubject;
1892
        }
1893
        if( ! $message) {
1894
            $message = $step->CustomerMessage;
1895
        }
1896
        $subject = str_replace('[OrderNumber]', $this->ID, $subject);
1897
        //set other variables
1898
        $replacementArray['Subject'] = $subject;
1899
        $replacementArray['To'] = '';
1900
        $replacementArray['CC'] = '';
1901
        $replacementArray['BCC'] = '';
1902
        $replacementArray['OrderStepMessage'] = $message;
1903
        $replacementArray['Order'] = $this;
1904
        $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...
1905
        $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...
1906
        $replacementArray['CurrentDateAndTime'] = DBField::create_field('SS_Datetime', 'Now');
1907
        $replacementArray['BaseURL'] = Director::baseURL();
1908
        $arrayData = ArrayData::create($replacementArray);
1909
        $this->extend('updateReplacementArrayForEmail', $arrayData);
1910
1911
        return $arrayData;
1912
    }
1913
1914
/*******************************************************
1915
   * 6. ITEM MANAGEMENT
1916
*******************************************************/
1917
1918
    /**
1919
     * returns a list of Order Attributes by type.
1920
     *
1921
     * @param array | String $types
1922
     *
1923
     * @return ArrayList
1924
     */
1925
    public function getOrderAttributesByType($types)
1926
    {
1927
        if (!is_array($types) && is_string($types)) {
1928
            $types = array($types);
1929
        }
1930
        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...
1931
            user_error('wrong parameter (types) provided in Order::getOrderAttributesByTypes');
1932
        }
1933
        $al = new ArrayList();
1934
        $items = $this->Items();
1935
        foreach ($items as $item) {
1936
            if (in_array($item->OrderAttributeType(), $types)) {
1937
                $al->push($item);
1938
            }
1939
        }
1940
        $modifiers = $this->Modifiers();
1941
        foreach ($modifiers as $modifier) {
1942
            if (in_array($modifier->OrderAttributeType(), $types)) {
1943
                $al->push($modifier);
1944
            }
1945
        }
1946
1947
        return $al;
1948
    }
1949
1950
    /**
1951
     * Returns the items of the order.
1952
     * Items are the order items (products) and NOT the modifiers (discount, tax, etc...).
1953
     *
1954
     * N. B. this method returns Order Items
1955
     * also see Buaybles
1956
1957
     *
1958
     * @param string filter - where statement to exclude certain items OR ClassName (e.g. 'TaxModifier')
1959
     *
1960
     * @return DataList (OrderItems)
1961
     */
1962
    public function Items($filterOrClassName = '')
1963
    {
1964
        if (!$this->exists()) {
1965
            $this->write();
1966
        }
1967
1968
        return $this->itemsFromDatabase($filterOrClassName);
1969
    }
1970
1971
    /**
1972
     * @alias function of Items
1973
     *
1974
     * N. B. this method returns Order Items
1975
     * also see Buaybles
1976
     *
1977
     * @param string filter - where statement to exclude certain items.
1978
     * @alias for Items
1979
     * @return DataList (OrderItems)
1980
     */
1981
    public function OrderItems($filterOrClassName = '')
1982
    {
1983
        return $this->Items($filterOrClassName);
1984
    }
1985
1986
    /**
1987
     * returns the buyables asscoiated with the order items.
1988
     *
1989
     * NB. this method retursn buyables
1990
     *
1991
     * @param string filter - where statement to exclude certain items.
1992
     *
1993
     * @return ArrayList (Buyables)
1994
     */
1995
    public function Buyables($filterOrClassName = '')
1996
    {
1997
        $items = $this->Items($filterOrClassName);
1998
        $arrayList = new ArrayList();
1999
        foreach ($items as $item) {
2000
            $arrayList->push($item->Buyable());
2001
        }
2002
2003
        return $arrayList;
2004
    }
2005
2006
    /**
2007
     * Return all the {@link OrderItem} instances that are
2008
     * available as records in the database.
2009
     *
2010
     * @param string filter - where statement to exclude certain items,
2011
     *   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)
2012
     *
2013
     * @return DataList (OrderItems)
2014
     */
2015
    protected function itemsFromDatabase($filterOrClassName = '')
2016
    {
2017
        $className = 'OrderItem';
2018
        $extrafilter = '';
2019
        if ($filterOrClassName) {
2020
            if (class_exists($filterOrClassName)) {
2021
                $className = $filterOrClassName;
2022
            } else {
2023
                $extrafilter = " AND $filterOrClassName";
2024
            }
2025
        }
2026
2027
        return $className::get()->filter(array('OrderID' => $this->ID))->where($extrafilter);
2028
    }
2029
2030
    /**
2031
     * @alias for Modifiers
2032
     *
2033
     * @return DataList (OrderModifiers)
2034
     */
2035
    public function OrderModifiers()
2036
    {
2037
        return $this->Modifiers();
2038
    }
2039
2040
    /**
2041
     * Returns the modifiers of the order, if it hasn't been saved yet
2042
     * it returns the modifiers from session, if it has, it returns them
2043
     * from the DB entry. ONLY USE OUTSIDE ORDER.
2044
     *
2045
     * @param string filter - where statement to exclude certain items OR ClassName (e.g. 'TaxModifier')
2046
     *
2047
     * @return DataList (OrderModifiers)
2048
     */
2049
    public function Modifiers($filterOrClassName = '')
2050
    {
2051
        return $this->modifiersFromDatabase($filterOrClassName);
2052
    }
2053
2054
    /**
2055
     * Get all {@link OrderModifier} instances that are
2056
     * available as records in the database.
2057
     * NOTE: includes REMOVED Modifiers, so that they do not get added again...
2058
     *
2059
     * @param string filter - where statement to exclude certain items OR ClassName (e.g. 'TaxModifier')
2060
     *
2061
     * @return DataList (OrderModifiers)
2062
     */
2063
    protected function modifiersFromDatabase($filterOrClassName = '')
2064
    {
2065
        $className = 'OrderModifier';
2066
        $extrafilter = '';
2067
        if ($filterOrClassName) {
2068
            if (class_exists($filterOrClassName)) {
2069
                $className = $filterOrClassName;
2070
            } else {
2071
                $extrafilter = " AND $filterOrClassName";
2072
            }
2073
        }
2074
2075
        return $className::get()->where('"OrderAttribute"."OrderID" = '.$this->ID." $extrafilter");
2076
    }
2077
2078
    /**
2079
     * Calculates and updates all the order attributes.
2080
     *
2081
     * @param bool $recalculate - run it, even if it has run already
2082
     */
2083
    public function calculateOrderAttributes($recalculate = false)
2084
    {
2085
        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...
2086
            //submitted orders are NEVER recalculated.
2087
            //they are set in stone.
2088
        } elseif (self::get_needs_recalculating($this->ID) || $recalculate) {
2089
            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...
2090
                $this->ensureCorrectExchangeRate();
2091
                $this->calculateOrderItems($recalculate);
2092
                $this->calculateModifiers($recalculate);
2093
                $this->extend('onCalculateOrder');
2094
            }
2095
        }
2096
    }
2097
2098
    /**
2099
     * Calculates and updates all the product items.
2100
     *
2101
     * @param bool $recalculate - run it, even if it has run already
2102
     */
2103
    protected function calculateOrderItems($recalculate = false)
2104
    {
2105
        //check if order has modifiers already
2106
        //check /re-add all non-removable ones
2107
        //$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...
2108
        $orderItems = $this->itemsFromDatabase();
2109
        if ($orderItems->count()) {
2110
            foreach ($orderItems as $orderItem) {
2111
                if ($orderItem) {
2112
                    $orderItem->runUpdate($recalculate);
2113
                }
2114
            }
2115
        }
2116
        $this->extend('onCalculateOrderItems', $orderItems);
2117
    }
2118
2119
    /**
2120
     * Calculates and updates all the modifiers.
2121
     *
2122
     * @param bool $recalculate - run it, even if it has run already
2123
     */
2124
    protected function calculateModifiers($recalculate = false)
2125
    {
2126
        $createdModifiers = $this->modifiersFromDatabase();
2127
        if ($createdModifiers->count()) {
2128
            foreach ($createdModifiers as $modifier) {
2129
                if ($modifier) {
2130
                    $modifier->runUpdate($recalculate);
2131
                }
2132
            }
2133
        }
2134
        $this->extend('onCalculateModifiers', $createdModifiers);
2135
    }
2136
2137
    /**
2138
     * Returns the subtotal of the modifiers for this order.
2139
     * If a modifier appears in the excludedModifiers array, it is not counted.
2140
     *
2141
     * @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...
2142
     * @param bool         $stopAtExcludedModifier - when this flag is TRUE, we stop adding the modifiers when we reach an excluded modifier.
2143
     *
2144
     * @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...
2145
     */
2146
    public function ModifiersSubTotal($excluded = null, $stopAtExcludedModifier = false)
2147
    {
2148
        $total = 0;
2149
        $modifiers = $this->Modifiers();
2150
        if ($modifiers->count()) {
2151
            foreach ($modifiers as $modifier) {
2152
                if (!$modifier->IsRemoved()) { //we just double-check this...
2153
                    if (is_array($excluded) && in_array($modifier->ClassName, $excluded)) {
2154
                        if ($stopAtExcludedModifier) {
2155
                            break;
2156
                        }
2157
                        //do the next modifier
2158
                        continue;
2159
                    } elseif (is_string($excluded) && ($modifier->ClassName == $excluded)) {
2160
                        if ($stopAtExcludedModifier) {
2161
                            break;
2162
                        }
2163
                        //do the next modifier
2164
                        continue;
2165
                    }
2166
                    $total += $modifier->CalculationTotal();
2167
                }
2168
            }
2169
        }
2170
2171
        return $total;
2172
    }
2173
2174
    /**
2175
     * returns a modifier that is an instanceof the classname
2176
     * it extends.
2177
     *
2178
     * @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...
2179
     *
2180
     * @return DataObject (OrderModifier)
2181
     **/
2182
    public function RetrieveModifier($className)
2183
    {
2184
        $modifiers = $this->Modifiers();
2185
        if ($modifiers->count()) {
2186
            foreach ($modifiers as $modifier) {
2187
                if (is_a($modifier, Object::getCustomClass($className))) {
2188
                    return $modifier;
2189
                }
2190
            }
2191
        }
2192
    }
2193
2194
/*******************************************************
2195
   * 7. CRUD METHODS (e.g. canView, canEdit, canDelete, etc...)
2196
*******************************************************/
2197
2198
    /**
2199
     * @param Member $member
2200
     *
2201
     * @return DataObject (Member)
2202
     **/
2203
     //TODO: please comment why we make use of this function
2204
    protected function getMemberForCanFunctions(Member $member = null)
2205
    {
2206
        if (!$member) {
2207
            $member = Member::currentUser();
2208
        }
2209
        if (!$member) {
2210
            $member = new Member();
2211
            $member->ID = 0;
2212
        }
2213
2214
        return $member;
2215
    }
2216
2217
    /**
2218
     * @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...
2219
     *
2220
     * @return bool
2221
     **/
2222
    public function canCreate($member = null)
2223
    {
2224
        $member = $this->getMemberForCanFunctions($member);
2225
        $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...
2226
        if ($extended !== null) {
2227
            return $extended;
2228
        }
2229
        if ($member->exists()) {
2230
            return $member->IsShopAdmin();
2231
        }
2232
    }
2233
2234
    /**
2235
     * Standard SS method - can the current member view this order?
2236
     *
2237
     * @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...
2238
     *
2239
     * @return bool
2240
     **/
2241
    public function canView($member = null)
2242
    {
2243
        if (!$this->exists()) {
2244
            return true;
2245
        }
2246
        $member = $this->getMemberForCanFunctions($member);
2247
        //check if this has been "altered" in any DataExtension
2248
        $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...
2249
        if ($extended !== null) {
2250
            return $extended;
2251
        }
2252
        //is the member is a shop admin they can always view it
2253
        if (EcommerceRole::current_member_is_shop_admin($member)) {
2254
            return true;
2255
        }
2256
2257
        //is the member is a shop assistant they can always view it
2258
        if (EcommerceRole::current_member_is_shop_assistant($member)) {
2259
            return true;
2260
        }
2261
        //if the current member OWNS the order, (s)he can always view it.
2262
        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...
2263
            return true;
2264
        }
2265
        //it is the current order
2266
        if ($this->IsInSession()) {
2267
            //we do some additional CHECKS for session hackings!
2268
            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...
2269
                //can't view the order of another member!
2270
                //shop admin exemption is already captured.
2271
                //this is always true
2272
                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...
2273
                    return false;
2274
                }
2275
            } else {
2276
                //order belongs to someone, but current user is NOT logged in...
2277
                //this is allowed!
2278
                //the reason it is allowed is because we want to be able to
2279
                //add order to non-existing member
2280
                return true;
2281
            }
2282
        }
2283
2284
        return false;
2285
    }
2286
2287
    /**
2288
     * @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...
2289
     * @return bool
2290
     */
2291
    public function canOverrideCanView($member = null)
2292
    {
2293
        if ($this->canView($member)) {
2294
            //can view overrides any concerns
2295
            return true;
2296
        } else {
2297
            $tsOrder = strtotime($this->LastEdited);
2298
            $tsNow = time();
2299
            $minutes = EcommerceConfig::get('Order', 'minutes_an_order_can_be_viewed_without_logging_in');
2300
            if ($minutes && ((($tsNow - $tsOrder) / 60) < $minutes)) {
2301
2302
                //has the order been edited recently?
2303
                return true;
2304
            } elseif ($orderStep = $this->MyStep()) {
2305
2306
                // order is being processed ...
2307
                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...
2308
            }
2309
        }
2310
        return false;
2311
    }
2312
2313
    /**
2314
     * @return bool
2315
     */
2316
    public function IsInSession()
2317
    {
2318
        $orderInSession = ShoppingCart::session_order();
2319
2320
        return $orderInSession && $this->ID && $this->ID == $orderInSession->ID;
2321
    }
2322
2323
    /**
2324
     * returns a pseudo random part of the session id.
2325
     *
2326
     * @param int $size
2327
     *
2328
     * @return string
2329
     */
2330
    public function LessSecureSessionID($size = 7, $start = null)
2331
    {
2332
        if (!$start || $start < 0 || $start > (32 - $size)) {
2333
            $start = 0;
2334
        }
2335
2336
        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...
2337
    }
2338
    /**
2339
     *
2340
     * @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...
2341
     *
2342
     * @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...
2343
     **/
2344
    public function canViewAdminStuff($member = null)
2345
    {
2346
        $member = $this->getMemberForCanFunctions($member);
2347
        $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...
2348
        if ($extended !== null) {
2349
            return $extended;
2350
        }
2351
        if (Permission::checkMember($member, Config::inst()->get('EcommerceRole', 'admin_permission_code'))) {
2352
            return true;
2353
        }
2354
    }
2355
2356
    /**
2357
     * if we set canEdit to false then we
2358
     * can not see the child records
2359
     * Basically, you can edit when you can view and canEdit (even as a customer)
2360
     * Or if you are a Shop Admin you can always edit.
2361
     * Otherwise it is false...
2362
     *
2363
     * @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...
2364
     *
2365
     * @return bool
2366
     **/
2367
    public function canEdit($member = null)
2368
    {
2369
        $member = $this->getMemberForCanFunctions($member);
2370
        $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...
2371
        if ($extended !== null) {
2372
            return $extended;
2373
        }
2374
        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...
2375
            return true;
2376
        }
2377
        if (Permission::checkMember($member, Config::inst()->get('EcommerceRole', 'admin_permission_code'))) {
2378
            return true;
2379
        }
2380
        //is the member is a shop assistant they can always view it
2381
        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...
2382
            return true;
2383
        }
2384
        return false;
2385
    }
2386
2387
    /**
2388
     * is the order ready to go through to the
2389
     * checkout process.
2390
     *
2391
     * This method checks all the order items and order modifiers
2392
     * If any of them need immediate attention then this is done
2393
     * first after which it will go through to the checkout page.
2394
     *
2395
     * @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...
2396
     *
2397
     * @return bool
2398
     **/
2399
    public function canCheckout(Member $member = null)
2400
    {
2401
        $member = $this->getMemberForCanFunctions($member);
2402
        $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...
2403
        if ($extended !== null) {
2404
            return $extended;
2405
        }
2406
        $submitErrors = $this->SubmitErrors();
2407
        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...
2408
            return false;
2409
        }
2410
2411
        return true;
2412
    }
2413
2414
    /**
2415
     * Can the order be submitted?
2416
     * this method can be used to stop an order from being submitted
2417
     * due to something not being completed or done.
2418
     *
2419
     * @see Order::SubmitErrors
2420
     *
2421
     * @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...
2422
     *
2423
     * @return bool
2424
     **/
2425
    public function canSubmit(Member $member = null)
2426
    {
2427
        $member = $this->getMemberForCanFunctions($member);
2428
        $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...
2429
        if ($extended !== null) {
2430
            return $extended;
2431
        }
2432
        if ($this->IsSubmitted()) {
2433
            return false;
2434
        }
2435
        $submitErrors = $this->SubmitErrors();
2436
        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...
2437
            return false;
2438
        }
2439
2440
        return true;
2441
    }
2442
2443
    /**
2444
     * Can a payment be made for this Order?
2445
     *
2446
     * @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...
2447
     *
2448
     * @return bool
2449
     **/
2450
    public function canPay(Member $member = null)
2451
    {
2452
        $member = $this->getMemberForCanFunctions($member);
2453
        $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...
2454
        if ($extended !== null) {
2455
            return $extended;
2456
        }
2457
        if ($this->IsPaid() || $this->IsCancelled() || $this->PaymentIsPending()) {
2458
            return false;
2459
        }
2460
2461
        return $this->MyStep()->CustomerCanPay;
2462
    }
2463
2464
    /**
2465
     * Can the given member cancel this order?
2466
     *
2467
     * @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...
2468
     *
2469
     * @return bool
2470
     **/
2471
    public function canCancel(Member $member = null)
2472
    {
2473
        //if it is already cancelled it can not be cancelled again
2474
        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...
2475
            return false;
2476
        }
2477
        $member = $this->getMemberForCanFunctions($member);
2478
        $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...
2479
        if ($extended !== null) {
2480
            return $extended;
2481
        }
2482
        if (EcommerceRole::current_member_can_process_orders($member)) {
2483
            return true;
2484
        }
2485
2486
        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...
2487
    }
2488
2489
    /**
2490
     * @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...
2491
     *
2492
     * @return bool
2493
     **/
2494
    public function canDelete($member = null)
2495
    {
2496
        $member = $this->getMemberForCanFunctions($member);
2497
        $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...
2498
        if ($extended !== null) {
2499
            return $extended;
2500
        }
2501
        if ($this->IsSubmitted()) {
2502
            return false;
2503
        }
2504
        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...
2505
            return true;
2506
        }
2507
2508
        return false;
2509
    }
2510
2511
    /**
2512
     * Returns all the order logs that the current member can view
2513
     * i.e. some order logs can only be viewed by the admin (e.g. suspected fraud orderlog).
2514
     *
2515
     * @return ArrayList (OrderStatusLogs)
2516
     **/
2517
    public function CanViewOrderStatusLogs()
2518
    {
2519
        $canViewOrderStatusLogs = new ArrayList();
2520
        $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...
2521
        foreach ($logs as $log) {
2522
            if ($log->canView()) {
2523
                $canViewOrderStatusLogs->push($log);
2524
            }
2525
        }
2526
2527
        return $canViewOrderStatusLogs;
2528
    }
2529
2530
    /**
2531
     * returns all the logs that can be viewed by the customer.
2532
     *
2533
     * @return ArrayList (OrderStausLogs)
2534
     */
2535
    public function CustomerViewableOrderStatusLogs()
2536
    {
2537
        $customerViewableOrderStatusLogs = new ArrayList();
2538
        $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...
2539
        if ($logs) {
2540
            foreach ($logs as $log) {
2541
                if (!$log->InternalUseOnly) {
2542
                    $customerViewableOrderStatusLogs->push($log);
2543
                }
2544
            }
2545
        }
2546
2547
        return $customerViewableOrderStatusLogs;
2548
    }
2549
2550
/*******************************************************
2551
   * 8. GET METHODS (e.g. Total, SubTotal, Title, etc...)
2552
*******************************************************/
2553
2554
    /**
2555
     * returns the email to be used for customer communication.
2556
     *
2557
     * @return string
2558
     */
2559
    public function OrderEmail()
2560
    {
2561
        return $this->getOrderEmail();
2562
    }
2563
    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...
2564
    {
2565
        $email = '';
2566
        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...
2567
            $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...
2568
        }
2569
        if (! $email) {
2570
            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...
2571
                $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...
2572
            }
2573
        }
2574
        $extendedEmail = $this->extend('updateOrderEmail', $email);
2575
        if ($extendedEmail !== null && is_array($extendedEmail) && count($extendedEmail)) {
2576
            $email = implode(';', $extendedEmail);
2577
        }
2578
2579
        return $email;
2580
    }
2581
2582
    /**
2583
     * Returns true if there is a prink or email link.
2584
     *
2585
     * @return bool
2586
     */
2587
    public function HasPrintOrEmailLink()
2588
    {
2589
        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...
2590
    }
2591
2592
    /**
2593
     * returns the absolute link to the order that can be used in the customer communication (email).
2594
     *
2595
     * @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...
2596
     */
2597
    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...
2598
    {
2599
        return $this->getEmailLink();
2600
    }
2601
    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...
2602
    {
2603
        if (!isset($_REQUEST['print'])) {
2604
            if ($this->IsSubmitted()) {
2605
                return Director::AbsoluteURL(OrderConfirmationPage::get_email_link($this->ID, $this->MyStep()->getEmailClassName(), $actuallySendEmail = true));
2606
            }
2607
        }
2608
    }
2609
2610
    /**
2611
     * returns the absolute link to the order for printing.
2612
     *
2613
     * @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...
2614
     */
2615
    public function PrintLink()
2616
    {
2617
        return $this->getPrintLink();
2618
    }
2619
    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...
2620
    {
2621
        if (!isset($_REQUEST['print'])) {
2622
            if ($this->IsSubmitted()) {
2623
                return Director::AbsoluteURL(OrderConfirmationPage::get_order_link($this->ID)).'?print=1';
2624
            }
2625
        }
2626
    }
2627
2628
    /**
2629
     * returns the absolute link to the order for printing.
2630
     *
2631
     * @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...
2632
     */
2633
    public function PackingSlipLink()
2634
    {
2635
        return $this->getPackingSlipLink();
2636
    }
2637
    public function getPackingSlipLink()
2638
    {
2639
        if ($this->IsSubmitted()) {
2640
            return Director::AbsoluteURL(OrderConfirmationPage::get_order_link($this->ID)).'?packingslip=1';
2641
        }
2642
    }
2643
2644
    /**
2645
     * returns the absolute link that the customer can use to retrieve the email WITHOUT logging in.
2646
     *
2647
     * @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...
2648
     */
2649
    public function RetrieveLink()
2650
    {
2651
        return $this->getRetrieveLink();
2652
    }
2653
2654
    public function getRetrieveLink()
2655
    {
2656
        //important to recalculate!
2657
        if ($this->IsSubmitted($recalculate = true)) {
2658
            //add session ID if not added yet...
2659
            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...
2660
                $this->write();
2661
            }
2662
2663
            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...
2664
        } else {
2665
            return Director::AbsoluteURL('/shoppingcart/loadorder/'.$this->ID.'/');
2666
        }
2667
    }
2668
2669
    public function ShareLink()
2670
    {
2671
        return $this->getShareLink();
2672
    }
2673
2674
    public function getShareLink()
2675
    {
2676
        $orderItems = $this->itemsFromDatabase();
2677
        $action = 'share';
2678
        $array = array();
2679
        foreach ($orderItems as $orderItem) {
2680
            $array[] = implode(
2681
                ',',
2682
                array(
2683
                    $orderItem->BuyableClassName,
2684
                    $orderItem->BuyableID,
2685
                    $orderItem->Quantity
2686
                )
2687
            );
2688
        }
2689
2690
        return Director::AbsoluteURL(CartPage::find_link($action.'/'.implode('-', $array)));
2691
    }
2692
2693
    /**
2694
     * @alias for getFeedbackLink
2695
     * @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...
2696
     */
2697
    public function FeedbackLink()
2698
    {
2699
        return $this->getFeedbackLink();
2700
    }
2701
2702
    /**
2703
     * @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...
2704
     */
2705
    public function getFeedbackLink()
2706
    {
2707
        $orderConfirmationPage = DataObject::get_one('OrderConfirmationPage');
2708
        if($orderConfirmationPage->IsFeedbackEnabled) {
2709
2710
            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...
2711
        }
2712
    }
2713
2714
    /**
2715
     * link to delete order.
2716
     *
2717
     * @return string
2718
     */
2719
    public function DeleteLink()
2720
    {
2721
        return $this->getDeleteLink();
2722
    }
2723
    public function getDeleteLink()
2724
    {
2725
        if ($this->canDelete()) {
2726
            return ShoppingCart_Controller::delete_order_link($this->ID);
2727
        } else {
2728
            return '';
2729
        }
2730
    }
2731
2732
    /**
2733
     * link to copy order.
2734
     *
2735
     * @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...
2736
     */
2737
    public function CopyOrderLink()
2738
    {
2739
        return $this->getCopyOrderLink();
2740
    }
2741
    public function getCopyOrderLink()
2742
    {
2743
        if ($this->canView() && $this->IsSubmitted()) {
2744
            return ShoppingCart_Controller::copy_order_link($this->ID);
2745
        } else {
2746
            return '';
2747
        }
2748
    }
2749
2750
    /**
2751
     * A "Title" for the order, which summarises the main details (date, and customer) in a string.
2752
     *
2753
     * @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...
2754
     * @param bool   $includeName - e.g. by Mr Johnson
2755
     *
2756
     * @return string
2757
     **/
2758
    public function Title($dateFormat = null, $includeName = false)
2759
    {
2760
        return $this->getTitle($dateFormat, $includeName);
2761
    }
2762
    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...
2763
    {
2764
        if ($this->exists()) {
2765
            if ($dateFormat === null) {
2766
                $dateFormat = EcommerceConfig::get('Order', 'date_format_for_title');
2767
            }
2768
            if ($includeName === null) {
2769
                $includeName = EcommerceConfig::get('Order', 'include_customer_name_in_title');
2770
            }
2771
            $title = $this->i18n_singular_name()." #".number_format($this->ID);
2772
            if ($dateFormat) {
2773
                if ($submissionLog = $this->SubmissionLog()) {
2774
                    $dateObject = $submissionLog->dbObject('Created');
2775
                    $placed = _t('Order.PLACED', 'placed');
2776
                } else {
2777
                    $dateObject = $this->dbObject('Created');
2778
                    $placed = _t('Order.STARTED', 'started');
2779
                }
2780
                $title .= ' - '.$placed.' '.$dateObject->Format($dateFormat);
2781
            }
2782
            $name = '';
2783
            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...
2784
                $name = ' - '._t('Order.CANCELLED', 'CANCELLED');
2785
            }
2786
            if ($includeName) {
2787
                $by = _t('Order.BY', 'by');
2788
                if (!$name) {
2789
                    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...
2790
                        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...
2791
                            $name = ' - '.$by.' '.$billingAddress->Prefix.' '.$billingAddress->FirstName.' '.$billingAddress->Surname;
2792
                        }
2793
                    }
2794
                }
2795
                if (!$name) {
2796
                    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...
2797
                        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...
2798
                            if ($member->exists()) {
2799
                                if ($memberName = $member->getName()) {
2800
                                    if (!trim($memberName)) {
2801
                                        $memberName = _t('Order.ANONYMOUS', 'anonymous');
2802
                                    }
2803
                                    $name = ' - '.$by.' '.$memberName;
2804
                                }
2805
                            }
2806
                        }
2807
                    }
2808
                }
2809
            }
2810
            $title .= $name;
2811
        } else {
2812
            $title = _t('Order.NEW', 'New').' '.$this->i18n_singular_name();
2813
        }
2814
        $extendedTitle = $this->extend('updateTitle', $title);
2815
        if ($extendedTitle !== null && is_array($extendedTitle) && count($extendedTitle)) {
2816
            $title = implode('; ', $extendedTitle);
2817
        }
2818
2819
        return $title;
2820
    }
2821
2822
    /**
2823
     * Returns the subtotal of the items for this order.
2824
     *
2825
     * @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...
2826
     */
2827
    public function SubTotal()
2828
    {
2829
        return $this->getSubTotal();
2830
    }
2831
    public function getSubTotal()
2832
    {
2833
        $result = 0;
2834
        $items = $this->Items();
2835
        if ($items->count()) {
2836
            foreach ($items as $item) {
2837
                if (is_a($item, Object::getCustomClass('OrderAttribute'))) {
2838
                    $result += $item->Total();
2839
                }
2840
            }
2841
        }
2842
2843
        return $result;
2844
    }
2845
2846
    /**
2847
     * @return Currency (DB Object)
2848
     **/
2849
    public function SubTotalAsCurrencyObject()
2850
    {
2851
        return DBField::create_field('Currency', $this->SubTotal());
2852
    }
2853
2854
    /**
2855
     * @return Money
2856
     **/
2857
    public function SubTotalAsMoney()
2858
    {
2859
        return $this->getSubTotalAsMoney();
2860
    }
2861
    public function getSubTotalAsMoney()
2862
    {
2863
        return EcommerceCurrency::get_money_object_from_order_currency($this->SubTotal(), $this);
2864
    }
2865
2866
    /**
2867
     * @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...
2868
     * @param bool         $stopAtExcludedModifier - when this flag is TRUE, we stop adding the modifiers when we reach an excluded modifier.
2869
     *
2870
     * @return Currency (DB Object)
2871
     **/
2872
    public function ModifiersSubTotalAsCurrencyObject($excluded = null, $stopAtExcludedModifier = false)
2873
    {
2874
        return DBField::create_field('Currency', $this->ModifiersSubTotal($excluded, $stopAtExcludedModifier));
2875
    }
2876
2877
    /**
2878
     * @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...
2879
     * @param bool         $stopAtExcludedModifier - when this flag is TRUE, we stop adding the modifiers when we reach an excluded modifier.
2880
     *
2881
     * @return Money (DB Object)
2882
     **/
2883
    public function ModifiersSubTotalAsMoneyObject($excluded = null, $stopAtExcludedModifier = false)
2884
    {
2885
        return EcommerceCurrency::get_money_object_from_order_currency($this->ModifiersSubTotal($excluded, $stopAtExcludedModifier), $this);
2886
    }
2887
2888
    /**
2889
     * Returns the total cost of an order including the additional charges or deductions of its modifiers.
2890
     *
2891
     * @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...
2892
     */
2893
    public function Total()
2894
    {
2895
        return $this->getTotal();
2896
    }
2897
    public function getTotal()
2898
    {
2899
        return $this->SubTotal() + $this->ModifiersSubTotal();
2900
    }
2901
2902
    /**
2903
     * @return Currency (DB Object)
2904
     **/
2905
    public function TotalAsCurrencyObject()
2906
    {
2907
        return DBField::create_field('Currency', $this->Total());
2908
    }
2909
2910
    /**
2911
     * @return Money
2912
     **/
2913
    public function TotalAsMoney()
2914
    {
2915
        return $this->getTotalAsMoney();
2916
    }
2917
    public function getTotalAsMoney()
2918
    {
2919
        return EcommerceCurrency::get_money_object_from_order_currency($this->Total(), $this);
2920
    }
2921
2922
    /**
2923
     * Checks to see if any payments have been made on this order
2924
     * and if so, subracts the payment amount from the order.
2925
     *
2926
     * @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...
2927
     **/
2928
    public function TotalOutstanding()
2929
    {
2930
        return $this->getTotalOutstanding();
2931
    }
2932
    public function getTotalOutstanding()
2933
    {
2934
        if ($this->IsSubmitted()) {
2935
            $total = $this->Total();
2936
            $paid = $this->TotalPaid();
2937
            $outstanding = $total - $paid;
2938
            $maxDifference = EcommerceConfig::get('Order', 'maximum_ignorable_sales_payments_difference');
2939
            if (abs($outstanding) < $maxDifference) {
2940
                $outstanding = 0;
2941
            }
2942
2943
            return floatval($outstanding);
2944
        } else {
2945
            return 0;
2946
        }
2947
    }
2948
2949
    /**
2950
     * @return Currency (DB Object)
2951
     **/
2952
    public function TotalOutstandingAsCurrencyObject()
2953
    {
2954
        return DBField::create_field('Currency', $this->TotalOutstanding());
2955
    }
2956
2957
    /**
2958
     * @return Money
2959
     **/
2960
    public function TotalOutstandingAsMoney()
2961
    {
2962
        return $this->getTotalOutstandingAsMoney();
2963
    }
2964
    public function getTotalOutstandingAsMoney()
2965
    {
2966
        return EcommerceCurrency::get_money_object_from_order_currency($this->TotalOutstanding(), $this);
2967
    }
2968
2969
    /**
2970
     * @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...
2971
     */
2972
    public function TotalPaid()
2973
    {
2974
        return $this->getTotalPaid();
2975
    }
2976
    public function getTotalPaid()
2977
    {
2978
        $paid = 0;
2979
        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...
2980
            foreach ($payments as $payment) {
2981
                if ($payment->Status == 'Success') {
2982
                    $paid += $payment->Amount->getAmount();
2983
                }
2984
            }
2985
        }
2986
        $reverseExchange = 1;
2987
        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...
2988
            $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...
2989
        }
2990
2991
        return $paid * $reverseExchange;
2992
    }
2993
2994
    /**
2995
     * @return Currency (DB Object)
2996
     **/
2997
    public function TotalPaidAsCurrencyObject()
2998
    {
2999
        return DBField::create_field('Currency', $this->TotalPaid());
3000
    }
3001
3002
    /**
3003
     * @return Money
3004
     **/
3005
    public function TotalPaidAsMoney()
3006
    {
3007
        return $this->getTotalPaidAsMoney();
3008
    }
3009
    public function getTotalPaidAsMoney()
3010
    {
3011
        return EcommerceCurrency::get_money_object_from_order_currency($this->TotalPaid(), $this);
3012
    }
3013
3014
    /**
3015
     * returns the total number of OrderItems (not modifiers).
3016
     * This is meant to run as fast as possible to quickly check
3017
     * if there is anything in the cart.
3018
     *
3019
     * @param bool $recalculate - do we need to recalculate (value is retained during lifetime of Object)
3020
     *
3021
     * @return int
3022
     **/
3023
    public function TotalItems($recalculate = false)
3024
    {
3025
        return $this->getTotalItems($recalculate);
3026
    }
3027
    public function getTotalItems($recalculate = false)
3028
    {
3029
        if ($this->totalItems === null || $recalculate) {
3030
            $this->totalItems = OrderItem::get()
3031
                ->where('"OrderAttribute"."OrderID" = '.$this->ID.' AND "OrderItem"."Quantity" > 0')
3032
                ->count();
3033
        }
3034
3035
        return $this->totalItems;
3036
    }
3037
3038
    /**
3039
     * Little shorthand.
3040
     *
3041
     * @param bool $recalculate
3042
     *
3043
     * @return bool
3044
     **/
3045
    public function MoreThanOneItemInCart($recalculate = false)
3046
    {
3047
        return $this->TotalItems($recalculate) > 1 ? true : false;
3048
    }
3049
3050
    /**
3051
     * returns the total number of OrderItems (not modifiers) times their respectective quantities.
3052
     *
3053
     * @param bool $recalculate - force recalculation
3054
     *
3055
     * @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...
3056
     **/
3057
    public function TotalItemsTimesQuantity($recalculate = false)
3058
    {
3059
        return $this->getTotalItemsTimesQuantity($recalculate);
3060
    }
3061
    public function getTotalItemsTimesQuantity($recalculate = false)
3062
    {
3063
        if ($this->totalItemsTimesQuantity === null || $recalculate) {
3064
            //to do, why do we check if you can edit ????
3065
            $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...
3066
                SELECT SUM("OrderItem"."Quantity")
3067
                FROM "OrderItem"
3068
                    INNER JOIN "OrderAttribute" ON "OrderAttribute"."ID" = "OrderItem"."ID"
3069
                WHERE
3070
                    "OrderAttribute"."OrderID" = '.$this->ID.'
3071
                    AND "OrderItem"."Quantity" > 0'
3072
            )->value();
3073
        }
3074
3075
        return $this->totalItemsTimesQuantity - 0;
3076
    }
3077
3078
    /**
3079
     *
3080
     * @return string (country code)
3081
     **/
3082
    public function Country()
3083
    {
3084
        return $this->getCountry();
3085
    }
3086
3087
    /**
3088
    * Returns the country code for the country that applies to the order.
3089
    * @alias  for getCountry
3090
    *
3091
    * @return string - country code e.g. NZ
3092
     */
3093
    public function getCountry()
3094
    {
3095
        $countryCodes = array(
3096
            'Billing' => '',
3097
            'Shipping' => '',
3098
        );
3099
        $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...
3100
        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...
3101
            $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...
3102
            if ($billingAddress) {
3103
                if ($billingAddress->Country) {
3104
                    $countryCodes['Billing'] = $billingAddress->Country;
3105
                }
3106
            }
3107
        }
3108
        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...
3109
            $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...
3110
            if ($shippingAddress) {
3111
                if ($shippingAddress->ShippingCountry) {
3112
                    $countryCodes['Shipping'] = $shippingAddress->ShippingCountry;
3113
                }
3114
            }
3115
        }
3116
        if (
3117
            (EcommerceConfig::get('OrderAddress', 'use_shipping_address_for_main_region_and_country') && $countryCodes['Shipping'])
3118
            ||
3119
            (!$countryCodes['Billing'] && $countryCodes['Shipping'])
3120
        ) {
3121
            $code = $countryCodes['Shipping'];
3122
        } elseif ($countryCodes['Billing']) {
3123
            $code = $countryCodes['Billing'];
3124
        } else {
3125
            $code = EcommerceCountry::get_country_from_ip();
3126
        }
3127
3128
        return $code;
3129
    }
3130
3131
    /**
3132
     * @alias for getFullNameCountry
3133
     *
3134
     * @return string - country name
3135
     **/
3136
    public function FullNameCountry()
3137
    {
3138
        return $this->getFullNameCountry();
3139
    }
3140
3141
    /**
3142
     * returns name of coutry.
3143
     *
3144
     * @return string - country name
3145
     **/
3146
    public function getFullNameCountry()
3147
    {
3148
        return EcommerceCountry::find_title($this->Country());
3149
    }
3150
3151
    /**
3152
     * @alis for getExpectedCountryName
3153
     * @return string - country name
3154
     **/
3155
    public function ExpectedCountryName()
3156
    {
3157
        return $this->getExpectedCountryName();
3158
    }
3159
3160
    /**
3161
     * returns name of coutry that we expect the customer to have
3162
     * This takes into consideration more than just what has been entered
3163
     * for example, it looks at GEO IP.
3164
     *
3165
     * @todo: why do we dont return a string IF there is only one item.
3166
     *
3167
     * @return string - country name
3168
     **/
3169
    public function getExpectedCountryName()
3170
    {
3171
        return EcommerceCountry::find_title(EcommerceCountry::get_country(false, $this->ID));
3172
    }
3173
3174
    /**
3175
     * return the title of the fixed country (if any).
3176
     *
3177
     * @return string | empty string
3178
     **/
3179
    public function FixedCountry()
3180
    {
3181
        return $this->getFixedCountry();
3182
    }
3183
    public function getFixedCountry()
3184
    {
3185
        $code = EcommerceCountry::get_fixed_country_code();
3186
        if ($code) {
3187
            return EcommerceCountry::find_title($code);
3188
        }
3189
3190
        return '';
3191
    }
3192
3193
    /**
3194
     * Returns the region that applies to the order.
3195
     * we check both billing and shipping, in case one of them is empty.
3196
     *
3197
     * @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...
3198
     **/
3199
    public function Region()
3200
    {
3201
        return $this->getRegion();
3202
    }
3203
    public function getRegion()
3204
    {
3205
        $regionIDs = array(
3206
            'Billing' => 0,
3207
            'Shipping' => 0,
3208
        );
3209
        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...
3210
            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...
3211
                if ($billingAddress->RegionID) {
3212
                    $regionIDs['Billing'] = $billingAddress->RegionID;
3213
                }
3214
            }
3215
        }
3216
        if ($this->CanHaveShippingAddress()) {
3217
            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...
3218
                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...
3219
                    if ($shippingAddress->ShippingRegionID) {
3220
                        $regionIDs['Shipping'] = $shippingAddress->ShippingRegionID;
3221
                    }
3222
                }
3223
            }
3224
        }
3225
        if (count($regionIDs)) {
3226
            //note the double-check with $this->CanHaveShippingAddress() and get_use_....
3227
            if ($this->CanHaveShippingAddress() && EcommerceConfig::get('OrderAddress', 'use_shipping_address_for_main_region_and_country') && $regionIDs['Shipping']) {
3228
                return EcommerceRegion::get()->byID($regionIDs['Shipping']);
3229
            } else {
3230
                return EcommerceRegion::get()->byID($regionIDs['Billing']);
3231
            }
3232
        } else {
3233
            return EcommerceRegion::get()->byID(EcommerceRegion::get_region_from_ip());
3234
        }
3235
    }
3236
3237
    /**
3238
     * Casted variable
3239
     * Currency is not the same as the standard one?
3240
     *
3241
     * @return bool
3242
     **/
3243
    public function HasAlternativeCurrency()
3244
    {
3245
        return $this->getHasAlternativeCurrency();
3246
    }
3247
    public function getHasAlternativeCurrency()
3248
    {
3249
        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...
3250
            if ($currency->IsDefault()) {
0 ignored issues
show
Coding Style introduced by
The if-else statement can be simplified to return !$currency->IsDefault();.
Loading history...
3251
                return false;
3252
            } else {
3253
                return true;
3254
            }
3255
        } else {
3256
            return false;
3257
        }
3258
    }
3259
3260
    /**
3261
     * Makes sure exchange rate is updated and maintained before order is submitted
3262
     * This method is public because it could be called from a shopping Cart Object.
3263
     **/
3264
    public function EnsureCorrectExchangeRate()
3265
    {
3266
        if (!$this->IsSubmitted()) {
3267
            $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...
3268
            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...
3269
                if ($currency->IsDefault()) {
3270
                    $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...
3271
                } else {
3272
                    $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...
3273
                }
3274
            } else {
3275
                $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...
3276
            }
3277
            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...
3278
                $this->write();
3279
            }
3280
        }
3281
    }
3282
3283
    /**
3284
     * speeds up processing by storing the IsSubmitted value
3285
     * we start with -1 to know if it has been requested before.
3286
     *
3287
     * @var bool
3288
     */
3289
    protected $_isSubmittedTempVar = -1;
3290
3291
    /**
3292
     * Casted variable - has the order been submitted?
3293
     * alias
3294
     * @param bool $recalculate
3295
     *
3296
     * @return bool
3297
     **/
3298
    public function IsSubmitted($recalculate = true)
3299
    {
3300
        return $this->getIsSubmitted($recalculate);
3301
    }
3302
3303
    /**
3304
     * Casted variable - has the order been submitted?
3305
     *
3306
     * @param bool $recalculate
3307
     *
3308
     * @return bool
3309
     **/
3310
    public function getIsSubmitted($recalculate = false)
3311
    {
3312
        if ($this->_isSubmittedTempVar === -1 || $recalculate) {
3313
            if ($this->SubmissionLog()) {
3314
                $this->_isSubmittedTempVar = true;
3315
            } else {
3316
                $this->_isSubmittedTempVar = false;
3317
            }
3318
        }
3319
3320
        return $this->_isSubmittedTempVar;
3321
    }
3322
3323
    /**
3324
     *
3325
     *
3326
     * @return bool
3327
     */
3328
    public function IsArchived()
3329
    {
3330
        $lastStep = OrderStep::get()->Last();
3331
        if ($lastStep) {
3332
            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...
3333
                return true;
3334
            }
3335
        }
3336
        return false;
3337
    }
3338
3339
    /**
3340
     * Submission Log for this Order (if any).
3341
     *
3342
     * @return Submission Log (OrderStatusLog_Submitted) | Null
3343
     **/
3344
    public function SubmissionLog()
3345
    {
3346
        $className = EcommerceConfig::get('OrderStatusLog', 'order_status_log_class_used_for_submitting_order');
3347
3348
        return $className::get()
3349
            ->Filter(array('OrderID' => $this->ID))
3350
            ->Last();
3351
    }
3352
3353
    /**
3354
     * Submission Log for this Order (if any).
3355
     *
3356
     * @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...
3357
     **/
3358
    public function OrderDate()
3359
    {
3360
        $object = $this->SubmissionLog();
3361
        if($object) {
3362
            $created = $object->Created;
3363
        } else {
3364
            $created = $this->LastEdited;
3365
        }
3366
3367
        return DBField::create_field('SS_Datetime', $created);
3368
    }
3369
3370
    /**
3371
     * @return int
3372
     */
3373
    public function SecondsSinceBeingSubmitted()
3374
    {
3375
        if ($submissionLog = $this->SubmissionLog()) {
3376
            return time() - strtotime($submissionLog->Created);
3377
        } else {
3378
            return 0;
3379
        }
3380
    }
3381
3382
    /**
3383
     * if the order can not be submitted,
3384
     * then the reasons why it can not be submitted
3385
     * will be returned by this method.
3386
     *
3387
     * @see Order::canSubmit
3388
     *
3389
     * @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...
3390
     */
3391
    public function SubmitErrors()
3392
    {
3393
        $al = null;
3394
        $extendedSubmitErrors = $this->extend('updateSubmitErrors');
3395
        if ($extendedSubmitErrors !== null && is_array($extendedSubmitErrors) && count($extendedSubmitErrors)) {
3396
            $al = ArrayList::create();
3397
            foreach ($extendedSubmitErrors as $returnResultArray) {
3398
                foreach ($returnResultArray as $issue) {
3399
                    if ($issue) {
3400
                        $al->push(ArrayData::create(array("Title" => $issue)));
3401
                    }
3402
                }
3403
            }
3404
        }
3405
        return $al;
3406
    }
3407
3408
    /**
3409
     * Casted variable - has the order been submitted?
3410
     *
3411
     * @param bool $withDetail
3412
     *
3413
     * @return string
3414
     **/
3415
    public function CustomerStatus($withDetail = true)
3416
    {
3417
        return $this->getCustomerStatus($withDetail);
3418
    }
3419
    public function getCustomerStatus($withDetail = true)
3420
    {
3421
        $str = '';
3422
        if ($this->MyStep()->ShowAsUncompletedOrder) {
3423
            $str = _t('Order.UNCOMPLETED', 'Uncompleted');
3424
        } elseif ($this->MyStep()->ShowAsInProcessOrder) {
3425
            $str = _t('Order.IN_PROCESS', 'In Process');
3426
        } elseif ($this->MyStep()->ShowAsCompletedOrder) {
3427
            $str = _t('Order.COMPLETED', 'Completed');
3428
        }
3429
        if ($withDetail) {
3430
            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...
3431
                $str .= ' ('.$this->MyStep()->Name.')';
3432
            }
3433
        }
3434
3435
        return $str;
3436
    }
3437
3438
    /**
3439
     * Casted variable - does the order have a potential shipping address?
3440
     *
3441
     * @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...
3442
     **/
3443
    public function CanHaveShippingAddress()
3444
    {
3445
        return $this->getCanHaveShippingAddress();
3446
    }
3447
    public function getCanHaveShippingAddress()
3448
    {
3449
        return EcommerceConfig::get('OrderAddress', 'use_separate_shipping_address');
3450
    }
3451
3452
    /**
3453
     * returns the link to view the Order
3454
     * WHY NOT CHECKOUT PAGE: first we check for cart page.
3455
     *
3456
     * @return CartPage | Null
3457
     */
3458
    public function DisplayPage()
3459
    {
3460
        if ($this->MyStep() && $this->MyStep()->AlternativeDisplayPage()) {
3461
            $page = $this->MyStep()->AlternativeDisplayPage();
3462
        } elseif ($this->IsSubmitted()) {
3463
            $page = DataObject::get_one('OrderConfirmationPage');
3464
        } else {
3465
            $page = DataObject::get_one(
3466
                'CartPage',
3467
                array('ClassName' => 'CartPage')
3468
            );
3469
            if (!$page) {
3470
                $page = DataObject::get_one('CheckoutPage');
3471
            }
3472
        }
3473
3474
        return $page;
3475
    }
3476
3477
    /**
3478
     * returns the link to view the Order
3479
     * WHY NOT CHECKOUT PAGE: first we check for cart page.
3480
     * If a cart page has been created then we refer through to Cart Page.
3481
     * Otherwise it will default to the checkout page.
3482
     *
3483
     * @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...
3484
     *
3485
     * @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...
3486
     */
3487
    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...
3488
    {
3489
        $page = $this->DisplayPage();
3490
        if ($page) {
3491
            return $page->getOrderLink($this->ID, $action);
3492
        } else {
3493
            user_error('A Cart / Checkout Page + an Order Confirmation Page needs to be setup for the e-commerce module to work.', E_USER_NOTICE);
3494
            $page = DataObject::get_one(
3495
                'ErrorPage',
3496
                array('ErrorCode' => '404')
3497
            );
3498
            if ($page) {
3499
                return $page->Link();
3500
            }
3501
        }
3502
    }
3503
3504
    /**
3505
     * Returns to link to access the Order's API.
3506
     *
3507
     * @param string $version
3508
     * @param string $extension
3509
     *
3510
     * @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...
3511
     */
3512
    public function APILink($version = 'v1', $extension = 'xml')
3513
    {
3514
        return Director::AbsoluteURL("/api/ecommerce/$version/Order/".$this->ID."/.$extension");
3515
    }
3516
3517
    /**
3518
     * returns the link to finalise the Order.
3519
     *
3520
     * @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...
3521
     */
3522
    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...
3523
    {
3524
        $page = DataObject::get_one('CheckoutPage');
3525
        if ($page) {
3526
            return $page->Link();
3527
        } else {
3528
            $page = DataObject::get_one(
3529
                'ErrorPage',
3530
                array('ErrorCode' => '404')
3531
            );
3532
            if ($page) {
3533
                return $page->Link();
3534
            }
3535
        }
3536
    }
3537
3538
    /**
3539
     * Converts the Order into HTML, based on the Order Template.
3540
     *
3541
     * @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...
3542
     **/
3543
    public function ConvertToHTML()
3544
    {
3545
        Config::nest();
3546
        Config::inst()->update('SSViewer', 'theme_enabled', true);
3547
        $html = $this->renderWith('Order');
3548
        Config::unnest();
3549
        $html = preg_replace('/(\s)+/', ' ', $html);
3550
3551
        return DBField::create_field('HTMLText', $html);
3552
    }
3553
3554
    /**
3555
     * Converts the Order into a serialized string
3556
     * TO DO: check if this works and check if we need to use special sapphire serialization code.
3557
     *
3558
     * @return string - serialized object
3559
     **/
3560
    public function ConvertToString()
3561
    {
3562
        return serialize($this->addHasOneAndHasManyAsVariables());
3563
    }
3564
3565
    /**
3566
     * Converts the Order into a JSON object
3567
     * TO DO: check if this works and check if we need to use special sapphire JSON code.
3568
     *
3569
     * @return string -  JSON
3570
     **/
3571
    public function ConvertToJSON()
3572
    {
3573
        return json_encode($this->addHasOneAndHasManyAsVariables());
3574
    }
3575
3576
    /**
3577
     * returns itself wtih more data added as variables.
3578
     * We add has_one and has_many as variables like this: $this->MyHasOne_serialized = serialize($this->MyHasOne()).
3579
     *
3580
     * @return Order - with most important has one and has many items included as variables.
3581
     **/
3582
    protected function addHasOneAndHasManyAsVariables()
3583
    {
3584
        $object = clone $this;
3585
        $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...
3586
        $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...
3587
        $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...
3588
        $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...
3589
        $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...
3590
        $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...
3591
        $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...
3592
3593
        return $object;
3594
    }
3595
3596
/*******************************************************
3597
   * 9. TEMPLATE RELATED STUFF
3598
*******************************************************/
3599
3600
    /**
3601
     * returns the instance of EcommerceConfigAjax for use in templates.
3602
     * In templates, it is used like this:
3603
     * $EcommerceConfigAjax.TableID.
3604
     *
3605
     * @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...
3606
     **/
3607
    public function AJAXDefinitions()
3608
    {
3609
        return EcommerceConfigAjax::get_one($this);
3610
    }
3611
3612
    /**
3613
     * returns the instance of EcommerceDBConfig.
3614
     *
3615
     * @return EcommerceDBConfig
3616
     **/
3617
    public function EcomConfig()
3618
    {
3619
        return EcommerceDBConfig::current_ecommerce_db_config();
3620
    }
3621
3622
    /**
3623
     * Collects the JSON data for an ajax return of the cart.
3624
     *
3625
     * @param array $js
3626
     *
3627
     * @return array (for use in AJAX for JSON)
3628
     **/
3629
    public function updateForAjax(array $js)
3630
    {
3631
        $function = EcommerceConfig::get('Order', 'ajax_subtotal_format');
3632
        if (is_array($function)) {
3633
            list($function, $format) = $function;
3634
        }
3635
        $subTotal = $this->$function();
3636
        if (isset($format)) {
3637
            $subTotal = $subTotal->$format();
3638
            unset($format);
3639
        }
3640
        $function = EcommerceConfig::get('Order', 'ajax_total_format');
3641
        if (is_array($function)) {
3642
            list($function, $format) = $function;
3643
        }
3644
        $total = $this->$function();
3645
        if (isset($format)) {
3646
            $total = $total->$format();
3647
        }
3648
        $ajaxObject = $this->AJAXDefinitions();
3649
        $js[] = array(
3650
            't' => 'id',
3651
            's' => $ajaxObject->TableSubTotalID(),
3652
            'p' => 'innerHTML',
3653
            'v' => $subTotal,
3654
        );
3655
        $js[] = array(
3656
            't' => 'id',
3657
            's' => $ajaxObject->TableTotalID(),
3658
            'p' => 'innerHTML',
3659
            'v' => $total,
3660
        );
3661
        $js[] = array(
3662
            't' => 'class',
3663
            's' => $ajaxObject->TotalItemsClassName(),
3664
            'p' => 'innerHTML',
3665
            'v' => $this->TotalItems($recalculate = true),
3666
        );
3667
        $js[] = array(
3668
            't' => 'class',
3669
            's' => $ajaxObject->TotalItemsTimesQuantityClassName(),
3670
            'p' => 'innerHTML',
3671
            'v' => $this->TotalItemsTimesQuantity(),
3672
        );
3673
        $js[] = array(
3674
            't' => 'class',
3675
            's' => $ajaxObject->ExpectedCountryClassName(),
3676
            'p' => 'innerHTML',
3677
            'v' => $this->ExpectedCountryName(),
3678
        );
3679
3680
        return $js;
3681
    }
3682
3683
    /**
3684
     * @ToDO: move to more appropriate class
3685
     *
3686
     * @return float
3687
     **/
3688
    public function SubTotalCartValue()
3689
    {
3690
        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...
3691
    }
3692
3693
/*******************************************************
3694
   * 10. STANDARD SS METHODS (requireDefaultRecords, onBeforeDelete, etc...)
3695
*******************************************************/
3696
3697
    /**
3698
     *standard SS method.
3699
     **/
3700
    public function populateDefaults()
3701
    {
3702
        parent::populateDefaults();
3703
    }
3704
3705
    public function onBeforeWrite()
3706
    {
3707
        parent::onBeforeWrite();
3708
        if (! $this->getCanHaveShippingAddress()) {
3709
            $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...
3710
        }
3711
        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...
3712
            $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...
3713
        }
3714
        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...
3715
            $generator = Injector::inst()->create('RandomGenerator');
3716
            $token = $generator->randomToken('sha1');
3717
            $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...
3718
        }
3719
    }
3720
3721
    /**
3722
     * standard SS method
3723
     * adds the ability to update order after writing it.
3724
     **/
3725
    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...
3726
    {
3727
        parent::onAfterWrite();
3728
        //crucial!
3729
        self::set_needs_recalculating(true, $this->ID);
3730
        // quick double-check
3731
        if ($this->IsCancelled() && ! $this->IsArchived()) {
3732
            $this->Archive($avoidWrites = true);
3733
        }
3734
        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...
3735
            //do nothing
3736
        } else {
3737
            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...
3738
                $this->calculateOrderAttributes($recalculate = false);
3739
                if (EcommerceRole::current_member_is_shop_admin()) {
3740
                    if (isset($_REQUEST['SubmitOrderViaCMS'])) {
3741
                        $this->tryToFinaliseOrder();
3742
                        //just in case it writes again...
3743
                        unset($_REQUEST['SubmitOrderViaCMS']);
3744
                    }
3745
                }
3746
            }
3747
        }
3748
    }
3749
3750
    /**
3751
     *standard SS method.
3752
     *
3753
     * delete attributes, statuslogs, and payments
3754
     * THIS SHOULD NOT BE USED AS ORDERS SHOULD BE CANCELLED NOT DELETED
3755
     */
3756
    public function onBeforeDelete()
3757
    {
3758
        parent::onBeforeDelete();
3759
        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...
3760
            foreach ($attributes as $attribute) {
3761
                $attribute->delete();
3762
                $attribute->destroy();
3763
            }
3764
        }
3765
3766
        //THE REST WAS GIVING ERRORS - POSSIBLY DUE TO THE FUNNY RELATIONSHIP (one-one, two times...)
3767
        /*
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...
3768
        if($billingAddress = $this->BillingAddress()) {
3769
            if($billingAddress->exists()) {
3770
                $billingAddress->delete();
3771
                $billingAddress->destroy();
3772
            }
3773
        }
3774
        if($shippingAddress = $this->ShippingAddress()) {
3775
            if($shippingAddress->exists()) {
3776
                $shippingAddress->delete();
3777
                $shippingAddress->destroy();
3778
            }
3779
        }
3780
3781
        if($statuslogs = $this->OrderStatusLogs()){
3782
            foreach($statuslogs as $log){
3783
                $log->delete();
3784
                $log->destroy();
3785
            }
3786
        }
3787
        if($payments = $this->Payments()){
3788
            foreach($payments as $payment){
3789
                $payment->delete();
3790
                $payment->destroy();
3791
            }
3792
        }
3793
        if($emails = $this->Emails()) {
3794
            foreach($emails as $email){
3795
                $email->delete();
3796
                $email->destroy();
3797
            }
3798
        }
3799
        */
3800
    }
3801
3802
/*******************************************************
3803
   * 11. DEBUG
3804
*******************************************************/
3805
3806
    /**
3807
     * Debug helper method.
3808
     * Can be called from /shoppingcart/debug/.
3809
     *
3810
     * @return string
3811
     */
3812
    public function debug()
3813
    {
3814
        $this->calculateOrderAttributes(true);
3815
3816
        return EcommerceTaskDebugCart::debug_object($this);
3817
    }
3818
}
3819