Completed
Push — master ( 8a816a...ee94a5 )
by Nicolaas
04:21 queued 01:26
created

Order::CreateOrReturnExistingAddress()   D

Complexity

Conditions 13
Paths 211

Size

Total Lines 44
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
dl 0
loc 44
rs 4.518
c 0
b 0
f 0
eloc 28
nc 211
nop 2

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * @description:
5
 * The order class is a databound object for handling Orders within SilverStripe.
6
 * Note that it works closely with the ShoppingCart class, which accompanies the Order
7
 * until it has been paid for / confirmed by the user.
8
 *
9
 *
10
 * CONTENTS:
11
 * ----------------------------------------------
12
 * 1. CMS STUFF
13
 * 2. MAIN TRANSITION FUNCTIONS
14
 * 3. STATUS RELATED FUNCTIONS / SHORTCUTS
15
 * 4. LINKING ORDER WITH MEMBER AND ADDRESS
16
 * 5. CUSTOMER COMMUNICATION
17
 * 6. ITEM MANAGEMENT
18
 * 7. CRUD METHODS (e.g. canView, canEdit, canDelete, etc...)
19
 * 8. GET METHODS (e.g. Total, SubTotal, Title, etc...)
20
 * 9. TEMPLATE RELATED STUFF
21
 * 10. STANDARD SS METHODS (requireDefaultRecords, onBeforeDelete, etc...)
22
 * 11. DEBUG
23
 *
24
 * @authors: Nicolaas [at] Sunny Side Up .co.nz
25
 * @package: ecommerce
26
 * @sub-package: model
27
 * @inspiration: Silverstripe Ltd, Jeremy
28
 *
29
 * NOTE: This is the SQL for selecting orders in sequence of
30
 **/
31
class Order extends DataObject implements EditableEcommerceObject
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

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

namespace YourVendor;

class YourClass { }

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

Loading history...
32
{
33
    /**
34
     * API Control.
35
     *
36
     * @var array
37
     */
38
    private static $api_access = array(
0 ignored issues
show
Unused Code introduced by
The property $api_access is not used and could be removed.

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading history...
115
        'SessionID' => true,
116
    );
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::last_order_step();
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::last_order_step();
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
    /**
1477
     * Has the order been cancelled?
1478
     *
1479
     * @return bool
1480
     */
1481
    public function IsCancelled()
1482
    {
1483
        return $this->getIsCancelled();
1484
    }
1485
    public function getIsCancelled()
1486
    {
1487
        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...
1488
    }
1489
1490
    /**
1491
     * Has the order been cancelled by the customer?
1492
     *
1493
     * @return bool
1494
     */
1495
    public function IsCustomerCancelled()
1496
    {
1497
        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...
1498
            return true;
1499
        }
1500
1501
        return false;
1502
    }
1503
1504
    /**
1505
     * Has the order been cancelled by the  administrator?
1506
     *
1507
     * @return bool
1508
     */
1509
    public function IsAdminCancelled()
1510
    {
1511
        if ($this->IsCancelled()) {
1512
            if (!$this->IsCustomerCancelled()) {
1513
                $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...
1514
                if ($admin) {
1515
                    if ($admin->IsShopAdmin()) {
1516
                        return true;
1517
                    }
1518
                }
1519
            }
1520
        }
1521
1522
        return false;
1523
    }
1524
1525
    /**
1526
     * Is the Shop Closed for business?
1527
     *
1528
     * @return bool
1529
     */
1530
    public function ShopClosed()
1531
    {
1532
        return EcomConfig()->ShopClosed;
1533
    }
1534
1535
/*******************************************************
1536
   * 4. LINKING ORDER WITH MEMBER AND ADDRESS
1537
*******************************************************/
1538
1539
    /**
1540
     * Returns a member linked to the order.
1541
     * If a member is already linked, it will return the existing member.
1542
     * Otherwise it will return a new Member.
1543
     *
1544
     * Any new member is NOT written, because we dont want to create a new member unless we have to!
1545
     * We will not add a member to the order unless a new one is created in the checkout
1546
     * OR the member is logged in / logs in.
1547
     *
1548
     * Also note that if a new member is created, it is not automatically written
1549
     *
1550
     * @param bool $forceCreation - if set to true then the member will always be saved in the database.
1551
     *
1552
     * @return Member
1553
     **/
1554
    public function CreateOrReturnExistingMember($forceCreation = false)
1555
    {
1556
        if ($this->IsSubmitted()) {
1557
            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...
1558
        }
1559
        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...
1560
            $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...
1561
        } elseif ($member = Member::currentUser()) {
1562
            if (!$member->IsShopAdmin()) {
1563
                $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...
1564
                $this->write();
1565
            }
1566
        }
1567
        $member = $this->Member();
0 ignored issues
show
Bug introduced by
The method Member() does not exist on Order. Did you maybe mean CreateOrReturnExistingMember()?

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

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

Loading history...
1568
        if (!$member) {
1569
            $member = new Member();
1570
        }
1571
        if ($member && $forceCreation) {
1572
            $member->write();
1573
        }
1574
1575
        return $member;
1576
    }
1577
1578
    /**
1579
     * Returns either the existing one or a new Order Address...
1580
     * All Orders will have a Shipping and Billing address attached to it.
1581
     * Method used to retrieve object e.g. for $order->BillingAddress(); "BillingAddress" is the method name you can use.
1582
     * If the method name is the same as the class name then dont worry about providing one.
1583
     *
1584
     * @param string $className             - ClassName of the Address (e.g. BillingAddress or ShippingAddress)
1585
     * @param string $alternativeMethodName - method to retrieve Address
1586
     **/
1587
    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...
1588
    {
1589
        if ($this->exists()) {
1590
            $methodName = $className;
1591
            if ($alternativeMethodName) {
1592
                $methodName = $alternativeMethodName;
1593
            }
1594
            if ($this->IsSubmitted()) {
1595
                return $this->$methodName();
1596
            }
1597
            $variableName = $className.'ID';
1598
            $address = null;
1599
            if ($this->$variableName) {
1600
                $address = $this->$methodName();
1601
            }
1602
            if (!$address) {
1603
                $address = new $className();
1604
                if ($member = $this->CreateOrReturnExistingMember()) {
1605
                    if ($member->exists()) {
1606
                        $address->FillWithLastAddressFromMember($member, $write = false);
1607
                    }
1608
                }
1609
            }
1610
            if ($address) {
1611
                if (!$address->exists()) {
1612
                    $address->write();
1613
                }
1614
                if ($address->OrderID != $this->ID) {
1615
                    $address->OrderID = $this->ID;
1616
                    $address->write();
1617
                }
1618
                if ($this->$variableName != $address->ID) {
1619
                    if (!$this->IsSubmitted()) {
1620
                        $this->$variableName = $address->ID;
1621
                        $this->write();
1622
                    }
1623
                }
1624
1625
                return $address;
1626
            }
1627
        }
1628
1629
        return;
1630
    }
1631
1632
    /**
1633
     * Sets the country in the billing and shipping address.
1634
     *
1635
     * @param string $countryCode            - code for the country e.g. NZ
1636
     * @param bool   $includeBillingAddress
1637
     * @param bool   $includeShippingAddress
1638
     **/
1639
    public function SetCountryFields($countryCode, $includeBillingAddress = true, $includeShippingAddress = true)
1640
    {
1641
        if ($this->IsSubmitted()) {
1642
            user_error('Can not change country in submitted order', E_USER_NOTICE);
1643
        } else {
1644
            if ($includeBillingAddress) {
1645
                if ($billingAddress = $this->CreateOrReturnExistingAddress('BillingAddress')) {
1646
                    $billingAddress->SetCountryFields($countryCode);
1647
                }
1648
            }
1649
            if (EcommerceConfig::get('OrderAddress', 'use_separate_shipping_address')) {
1650
                if ($includeShippingAddress) {
1651
                    if ($shippingAddress = $this->CreateOrReturnExistingAddress('ShippingAddress')) {
1652
                        $shippingAddress->SetCountryFields($countryCode);
1653
                    }
1654
                }
1655
            }
1656
        }
1657
    }
1658
1659
    /**
1660
     * Sets the region in the billing and shipping address.
1661
     *
1662
     * @param int $regionID - ID for the region to be set
1663
     **/
1664
    public function SetRegionFields($regionID)
1665
    {
1666
        if ($this->IsSubmitted()) {
1667
            user_error('Can not change country in submitted order', E_USER_NOTICE);
1668
        } else {
1669
            if ($billingAddress = $this->CreateOrReturnExistingAddress('BillingAddress')) {
1670
                $billingAddress->SetRegionFields($regionID);
1671
            }
1672
            if ($this->CanHaveShippingAddress()) {
1673
                if ($shippingAddress = $this->CreateOrReturnExistingAddress('ShippingAddress')) {
1674
                    $shippingAddress->SetRegionFields($regionID);
1675
                }
1676
            }
1677
        }
1678
    }
1679
1680
    /**
1681
     * Stores the preferred currency of the order.
1682
     * IMPORTANTLY we store the exchange rate for future reference...
1683
     *
1684
     * @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...
1685
     */
1686
    public function UpdateCurrency($newCurrency)
1687
    {
1688
        if ($this->IsSubmitted()) {
1689
            user_error('Can not set the currency after the order has been submitted', E_USER_NOTICE);
1690
        } else {
1691
            if (! is_a($newCurrency, Object::getCustomClass('EcommerceCurrency'))) {
1692
                $newCurrency = EcommerceCurrency::default_currency();
1693
            }
1694
            $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...
1695
            $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...
1696
            $this->write();
1697
        }
1698
    }
1699
1700
    /**
1701
     * alias for UpdateCurrency.
1702
     *
1703
     * @param EcommerceCurrency $currency
1704
     */
1705
    public function SetCurrency($currency)
1706
    {
1707
        $this->UpdateCurrency($currency);
1708
    }
1709
1710
/*******************************************************
1711
   * 5. CUSTOMER COMMUNICATION
1712
*******************************************************/
1713
1714
    /**
1715
     * Send the invoice of the order by email.
1716
     *
1717
     * @param string $emailClassName     (optional) class used to send email
1718
     * @param string $subject            (optional) subject for the email
1719
     * @param string $message            (optional) the main message in the email
1720
     * @param bool   $resend             (optional) send the email even if it has been sent before
1721
     * @param bool   $adminOnlyOrToEmail (optional) sends the email to the ADMIN ONLY, if you provide an email, it will go to the email...
1722
     *
1723
     * @return bool TRUE on success, FALSE on failure
1724
     */
1725
    public function sendEmail(
1726
        $emailClassName = 'Order_InvoiceEmail',
1727
        $subject = '',
1728
        $message = '',
1729
        $resend = false,
1730
        $adminOnlyOrToEmail = false
1731
    ) {
1732
        return $this->prepareAndSendEmail(
1733
            $emailClassName,
1734
            $subject,
1735
            $message,
1736
            $resend,
1737
            $adminOnlyOrToEmail
1738
        );
1739
    }
1740
1741
    /**
1742
     * Sends a message to the shop admin ONLY and not to the customer
1743
     * This can be used by ordersteps and orderlogs to notify the admin of any potential problems.
1744
     *
1745
     * @param string         $emailClassName       - (optional) template to be used ...
1746
     * @param string         $subject              - (optional) subject for the email
1747
     * @param string         $message              - (optional) message to be added with the email
1748
     * @param bool           $resend               - (optional) can it be sent twice?
1749
     * @param bool | string  $adminOnlyOrToEmail   - (optional) sends the email to the ADMIN ONLY, if you provide an email, it will go to the email...
1750
     *
1751
     * @return bool TRUE for success, FALSE for failure (not tested)
1752
     */
1753
    public function sendAdminNotification(
1754
        $emailClassName = 'Order_ErrorEmail',
1755
        $subject = '',
1756
        $message = '',
1757
        $resend = false,
1758
        $adminOnlyOrToEmail = true
1759
    ) {
1760
        return $this->prepareAndSendEmail(
1761
            $emailClassName,
1762
            $subject,
1763
            $message,
1764
            $resend,
1765
            $adminOnlyOrToEmail
1766
        );
1767
    }
1768
1769
    /**
1770
     * returns the order formatted as an email.
1771
     *
1772
     * @param string $emailClassName - template to use.
1773
     * @param string $subject        - (optional) the subject (which can be used as title in email)
1774
     * @param string $message        - (optional) the additional message
1775
     *
1776
     * @return string (html)
1777
     */
1778
    public function renderOrderInEmailFormat(
1779
        $emailClassName,
1780
        $subject = '',
1781
        $message = ''
1782
    )
1783
    {
1784
        $arrayData = $this->createReplacementArrayForEmail($subject, $message);
1785
        Config::nest();
1786
        Config::inst()->update('SSViewer', 'theme_enabled', true);
1787
        $html = $arrayData->renderWith($emailClassName);
1788
        Config::unnest();
1789
1790
        return Order_Email::emogrify_html($html);
1791
    }
1792
1793
    /**
1794
     * Send a mail of the order to the client (and another to the admin).
1795
     *
1796
     * @param string         $emailClassName       - (optional) template to be used ...
1797
     * @param string         $subject              - (optional) subject for the email
1798
     * @param string         $message              - (optional) message to be added with the email
1799
     * @param bool           $resend               - (optional) can it be sent twice?
1800
     * @param bool | string  $adminOnlyOrToEmail   - (optional) sends the email to the ADMIN ONLY, if you provide an email, it will go to the email...
1801
     *
1802
     * @return bool TRUE for success, FALSE for failure (not tested)
1803
     */
1804
    protected function prepareAndSendEmail(
1805
        $emailClassName = 'Order_InvoiceEmail',
1806
        $subject,
0 ignored issues
show
Coding Style introduced by
Parameters which have default values should be placed at the end.

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

// $a must always be passed; it's default value is never used.
function someFunction($a = 5, $b) { }
Loading history...
1807
        $message,
1808
        $resend = false,
1809
        $adminOnlyOrToEmail = false
1810
    ) {
1811
        $arrayData = $this->createReplacementArrayForEmail($subject, $message);
1812
        $from = Order_Email::get_from_email();
1813
        //why are we using this email and NOT the member.EMAIL?
1814
        //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...
1815
        if ($adminOnlyOrToEmail) {
1816
            if (filter_var($adminOnlyOrToEmail, FILTER_VALIDATE_EMAIL)) {
1817
                $to = $adminOnlyOrToEmail;
1818
                // invalid e-mail address
1819
            } else {
1820
                $to = Order_Email::get_from_email();
1821
            }
1822
        } else {
1823
            $to = $this->getOrderEmail();
1824
        }
1825
        if ($from && $to) {
1826
            if(! class_exists($emailClassName)) {
1827
                user_error('Invalid Email ClassName provided: '. $emailClassName, E_USER_ERROR);
1828
            }
1829
            $email = new $emailClassName();
1830
            if (!(is_a($email, Object::getCustomClass('Email')))) {
1831
                user_error('No correct email class provided.', E_USER_ERROR);
1832
            }
1833
            $email->setFrom($from);
1834
            $email->setTo($to);
1835
            //we take the subject from the Array Data, just in case it has been adjusted.
1836
            $email->setSubject($arrayData->getField('Subject'));
1837
            //we also see if a CC and a BCC have been added
1838
            ;
1839
            if ($cc = $arrayData->getField('CC')) {
1840
                $email->setCc($cc);
1841
            }
1842
            if ($bcc = $arrayData->getField('BCC')) {
1843
                $email->setBcc($bcc);
1844
            }
1845
            $email->populateTemplate($arrayData);
1846
            // This might be called from within the CMS,
1847
            // so we need to restore the theme, just in case
1848
            // templates within the theme exist
1849
            Config::nest();
1850
            Config::inst()->update('SSViewer', 'theme_enabled', true);
1851
            $email->setOrder($this);
1852
            $email->setResend($resend);
1853
            $result = $email->send(null);
1854
            Config::unnest();
1855
            if (Director::isDev()) {
1856
                return true;
1857
            } else {
1858
                return $result;
1859
            }
1860
        }
1861
1862
        return false;
1863
    }
1864
1865
    /**
1866
     * returns the Data that can be used in the body of an order Email
1867
     * we add the subject here so that the subject, for example, can be added to the <title>
1868
     * of the email template.
1869
     * we add the subject here so that the subject, for example, can be added to the <title>
1870
     * of the email template.
1871
     *
1872
     * @param string $subject  - (optional) subject for email
1873
     * @param string $message  - (optional) the additional message
1874
     *
1875
     * @return ArrayData
1876
     *                   - Subject - EmailSubject
1877
     *                   - Message - specific message for this order
1878
     *                   - Message - custom message
1879
     *                   - OrderStepMessage - generic message for step
1880
     *                   - Order
1881
     *                   - EmailLogo
1882
     *                   - ShopPhysicalAddress
1883
     *                   - CurrentDateAndTime
1884
     *                   - BaseURL
1885
     *                   - CC
1886
     *                   - BCC
1887
     */
1888
    protected function createReplacementArrayForEmail($subject = '', $message = '')
1889
    {
1890
        $step = $this->MyStep();
1891
        $config = $this->EcomConfig();
1892
        $replacementArray = array();
1893
        //set subject
1894
        if ( ! $subject) {
1895
            $subject = $step->EmailSubject;
1896
        }
1897
        if( ! $message) {
1898
            $message = $step->CustomerMessage;
1899
        }
1900
        $subject = str_replace('[OrderNumber]', $this->ID, $subject);
1901
        //set other variables
1902
        $replacementArray['Subject'] = $subject;
1903
        $replacementArray['To'] = '';
1904
        $replacementArray['CC'] = '';
1905
        $replacementArray['BCC'] = '';
1906
        $replacementArray['OrderStepMessage'] = $message;
1907
        $replacementArray['Order'] = $this;
1908
        $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...
1909
        $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...
1910
        $replacementArray['CurrentDateAndTime'] = DBField::create_field('SS_Datetime', 'Now');
1911
        $replacementArray['BaseURL'] = Director::baseURL();
1912
        $arrayData = ArrayData::create($replacementArray);
1913
        $this->extend('updateReplacementArrayForEmail', $arrayData);
1914
1915
        return $arrayData;
1916
    }
1917
1918
/*******************************************************
1919
   * 6. ITEM MANAGEMENT
1920
*******************************************************/
1921
1922
    /**
1923
     * returns a list of Order Attributes by type.
1924
     *
1925
     * @param array | String $types
1926
     *
1927
     * @return ArrayList
1928
     */
1929
    public function getOrderAttributesByType($types)
1930
    {
1931
        if (!is_array($types) && is_string($types)) {
1932
            $types = array($types);
1933
        }
1934
        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...
1935
            user_error('wrong parameter (types) provided in Order::getOrderAttributesByTypes');
1936
        }
1937
        $al = new ArrayList();
1938
        $items = $this->Items();
1939
        foreach ($items as $item) {
1940
            if (in_array($item->OrderAttributeType(), $types)) {
1941
                $al->push($item);
1942
            }
1943
        }
1944
        $modifiers = $this->Modifiers();
1945
        foreach ($modifiers as $modifier) {
1946
            if (in_array($modifier->OrderAttributeType(), $types)) {
1947
                $al->push($modifier);
1948
            }
1949
        }
1950
1951
        return $al;
1952
    }
1953
1954
    /**
1955
     * Returns the items of the order.
1956
     * Items are the order items (products) and NOT the modifiers (discount, tax, etc...).
1957
     *
1958
     * N. B. this method returns Order Items
1959
     * also see Buaybles
1960
1961
     *
1962
     * @param string filter - where statement to exclude certain items OR ClassName (e.g. 'TaxModifier')
1963
     *
1964
     * @return DataList (OrderItems)
1965
     */
1966
    public function Items($filterOrClassName = '')
1967
    {
1968
        if (!$this->exists()) {
1969
            $this->write();
1970
        }
1971
1972
        return $this->itemsFromDatabase($filterOrClassName);
1973
    }
1974
1975
    /**
1976
     * @alias function of Items
1977
     *
1978
     * N. B. this method returns Order Items
1979
     * also see Buaybles
1980
     *
1981
     * @param string filter - where statement to exclude certain items.
1982
     * @alias for Items
1983
     * @return DataList (OrderItems)
1984
     */
1985
    public function OrderItems($filterOrClassName = '')
1986
    {
1987
        return $this->Items($filterOrClassName);
1988
    }
1989
1990
    /**
1991
     * returns the buyables asscoiated with the order items.
1992
     *
1993
     * NB. this method retursn buyables
1994
     *
1995
     * @param string filter - where statement to exclude certain items.
1996
     *
1997
     * @return ArrayList (Buyables)
1998
     */
1999
    public function Buyables($filterOrClassName = '')
2000
    {
2001
        $items = $this->Items($filterOrClassName);
2002
        $arrayList = new ArrayList();
2003
        foreach ($items as $item) {
2004
            $arrayList->push($item->Buyable());
2005
        }
2006
2007
        return $arrayList;
2008
    }
2009
2010
    /**
2011
     * Return all the {@link OrderItem} instances that are
2012
     * available as records in the database.
2013
     *
2014
     * @param string filter - where statement to exclude certain items,
2015
     *   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)
2016
     *
2017
     * @return DataList (OrderItems)
2018
     */
2019
    protected function itemsFromDatabase($filterOrClassName = '')
2020
    {
2021
        $className = 'OrderItem';
2022
        $extrafilter = '';
2023
        if ($filterOrClassName) {
2024
            if (class_exists($filterOrClassName)) {
2025
                $className = $filterOrClassName;
2026
            } else {
2027
                $extrafilter = " AND $filterOrClassName";
2028
            }
2029
        }
2030
2031
        return $className::get()->filter(array('OrderID' => $this->ID))->where($extrafilter);
2032
    }
2033
2034
    /**
2035
     * @alias for Modifiers
2036
     *
2037
     * @return DataList (OrderModifiers)
2038
     */
2039
    public function OrderModifiers()
2040
    {
2041
        return $this->Modifiers();
2042
    }
2043
2044
    /**
2045
     * Returns the modifiers of the order, if it hasn't been saved yet
2046
     * it returns the modifiers from session, if it has, it returns them
2047
     * from the DB entry. ONLY USE OUTSIDE ORDER.
2048
     *
2049
     * @param string filter - where statement to exclude certain items OR ClassName (e.g. 'TaxModifier')
2050
     *
2051
     * @return DataList (OrderModifiers)
2052
     */
2053
    public function Modifiers($filterOrClassName = '')
2054
    {
2055
        return $this->modifiersFromDatabase($filterOrClassName);
2056
    }
2057
2058
    /**
2059
     * Get all {@link OrderModifier} instances that are
2060
     * available as records in the database.
2061
     * NOTE: includes REMOVED Modifiers, so that they do not get added again...
2062
     *
2063
     * @param string filter - where statement to exclude certain items OR ClassName (e.g. 'TaxModifier')
2064
     *
2065
     * @return DataList (OrderModifiers)
2066
     */
2067
    protected function modifiersFromDatabase($filterOrClassName = '')
2068
    {
2069
        $className = 'OrderModifier';
2070
        $extrafilter = '';
2071
        if ($filterOrClassName) {
2072
            if (class_exists($filterOrClassName)) {
2073
                $className = $filterOrClassName;
2074
            } else {
2075
                $extrafilter = " AND $filterOrClassName";
2076
            }
2077
        }
2078
2079
        return $className::get()->where('"OrderAttribute"."OrderID" = '.$this->ID." $extrafilter");
2080
    }
2081
2082
    /**
2083
     * Calculates and updates all the order attributes.
2084
     *
2085
     * @param bool $recalculate - run it, even if it has run already
2086
     */
2087
    public function calculateOrderAttributes($recalculate = false)
2088
    {
2089
        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...
2090
            //submitted orders are NEVER recalculated.
2091
            //they are set in stone.
2092
        } elseif (self::get_needs_recalculating($this->ID) || $recalculate) {
2093
            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...
2094
                $this->ensureCorrectExchangeRate();
2095
                $this->calculateOrderItems($recalculate);
2096
                $this->calculateModifiers($recalculate);
2097
                $this->extend('onCalculateOrder');
2098
            }
2099
        }
2100
    }
2101
2102
    /**
2103
     * Calculates and updates all the product items.
2104
     *
2105
     * @param bool $recalculate - run it, even if it has run already
2106
     */
2107
    protected function calculateOrderItems($recalculate = false)
2108
    {
2109
        //check if order has modifiers already
2110
        //check /re-add all non-removable ones
2111
        //$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...
2112
        $orderItems = $this->itemsFromDatabase();
2113
        if ($orderItems->count()) {
2114
            foreach ($orderItems as $orderItem) {
2115
                if ($orderItem) {
2116
                    $orderItem->runUpdate($recalculate);
2117
                }
2118
            }
2119
        }
2120
        $this->extend('onCalculateOrderItems', $orderItems);
2121
    }
2122
2123
    /**
2124
     * Calculates and updates all the modifiers.
2125
     *
2126
     * @param bool $recalculate - run it, even if it has run already
2127
     */
2128
    protected function calculateModifiers($recalculate = false)
2129
    {
2130
        $createdModifiers = $this->modifiersFromDatabase();
2131
        if ($createdModifiers->count()) {
2132
            foreach ($createdModifiers as $modifier) {
2133
                if ($modifier) {
2134
                    $modifier->runUpdate($recalculate);
2135
                }
2136
            }
2137
        }
2138
        $this->extend('onCalculateModifiers', $createdModifiers);
2139
    }
2140
2141
    /**
2142
     * Returns the subtotal of the modifiers for this order.
2143
     * If a modifier appears in the excludedModifiers array, it is not counted.
2144
     *
2145
     * @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...
2146
     * @param bool         $stopAtExcludedModifier - when this flag is TRUE, we stop adding the modifiers when we reach an excluded modifier.
2147
     *
2148
     * @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...
2149
     */
2150
    public function ModifiersSubTotal($excluded = null, $stopAtExcludedModifier = false)
2151
    {
2152
        $total = 0;
2153
        $modifiers = $this->Modifiers();
2154
        if ($modifiers->count()) {
2155
            foreach ($modifiers as $modifier) {
2156
                if (!$modifier->IsRemoved()) { //we just double-check this...
2157
                    if (is_array($excluded) && in_array($modifier->ClassName, $excluded)) {
2158
                        if ($stopAtExcludedModifier) {
2159
                            break;
2160
                        }
2161
                        //do the next modifier
2162
                        continue;
2163
                    } elseif (is_string($excluded) && ($modifier->ClassName == $excluded)) {
2164
                        if ($stopAtExcludedModifier) {
2165
                            break;
2166
                        }
2167
                        //do the next modifier
2168
                        continue;
2169
                    }
2170
                    $total += $modifier->CalculationTotal();
2171
                }
2172
            }
2173
        }
2174
2175
        return $total;
2176
    }
2177
2178
    /**
2179
     * returns a modifier that is an instanceof the classname
2180
     * it extends.
2181
     *
2182
     * @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...
2183
     *
2184
     * @return DataObject (OrderModifier)
2185
     **/
2186
    public function RetrieveModifier($className)
2187
    {
2188
        $modifiers = $this->Modifiers();
2189
        if ($modifiers->count()) {
2190
            foreach ($modifiers as $modifier) {
2191
                if (is_a($modifier, Object::getCustomClass($className))) {
2192
                    return $modifier;
2193
                }
2194
            }
2195
        }
2196
    }
2197
2198
/*******************************************************
2199
   * 7. CRUD METHODS (e.g. canView, canEdit, canDelete, etc...)
2200
*******************************************************/
2201
2202
    /**
2203
     * @param Member $member
2204
     *
2205
     * @return DataObject (Member)
2206
     **/
2207
     //TODO: please comment why we make use of this function
2208
    protected function getMemberForCanFunctions(Member $member = null)
2209
    {
2210
        if (!$member) {
2211
            $member = Member::currentUser();
2212
        }
2213
        if (!$member) {
2214
            $member = new Member();
2215
            $member->ID = 0;
2216
        }
2217
2218
        return $member;
2219
    }
2220
2221
    /**
2222
     * @param Member $member
0 ignored issues
show
Documentation introduced by
Should the type for parameter $member not be Member|null?

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

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

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

Loading history...
2223
     *
2224
     * @return bool
2225
     **/
2226
    public function canCreate($member = null)
2227
    {
2228
        $member = $this->getMemberForCanFunctions($member);
2229
        $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...
2230
        if ($extended !== null) {
2231
            return $extended;
2232
        }
2233
        if ($member->exists()) {
2234
            return $member->IsShopAdmin();
2235
        }
2236
    }
2237
2238
    /**
2239
     * Standard SS method - can the current member view this order?
2240
     *
2241
     * @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...
2242
     *
2243
     * @return bool
2244
     **/
2245
    public function canView($member = null)
2246
    {
2247
        if (!$this->exists()) {
2248
            return true;
2249
        }
2250
        $member = $this->getMemberForCanFunctions($member);
2251
        //check if this has been "altered" in any DataExtension
2252
        $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...
2253
        if ($extended !== null) {
2254
            return $extended;
2255
        }
2256
        //is the member is a shop admin they can always view it
2257
        if (EcommerceRole::current_member_is_shop_admin($member)) {
2258
            return true;
2259
        }
2260
2261
        //is the member is a shop assistant they can always view it
2262
        if (EcommerceRole::current_member_is_shop_assistant($member)) {
2263
            return true;
2264
        }
2265
        //if the current member OWNS the order, (s)he can always view it.
2266
        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...
2267
            return true;
2268
        }
2269
        //it is the current order
2270
        if ($this->IsInSession()) {
2271
            //we do some additional CHECKS for session hackings!
2272
            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...
2273
                //can't view the order of another member!
2274
                //shop admin exemption is already captured.
2275
                //this is always true
2276
                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...
2277
                    return false;
2278
                }
2279
            } else {
2280
                //order belongs to someone, but current user is NOT logged in...
2281
                //this is allowed!
2282
                //the reason it is allowed is because we want to be able to
2283
                //add order to non-existing member
2284
                return true;
2285
            }
2286
        }
2287
2288
        return false;
2289
    }
2290
2291
    /**
2292
     * @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...
2293
     * @return bool
2294
     */
2295
    public function canOverrideCanView($member = null)
2296
    {
2297
        if ($this->canView($member)) {
2298
            //can view overrides any concerns
2299
            return true;
2300
        } else {
2301
            $tsOrder = strtotime($this->LastEdited);
2302
            $tsNow = time();
2303
            $minutes = EcommerceConfig::get('Order', 'minutes_an_order_can_be_viewed_without_logging_in');
2304
            if ($minutes && ((($tsNow - $tsOrder) / 60) < $minutes)) {
2305
2306
                //has the order been edited recently?
2307
                return true;
2308
            } elseif ($orderStep = $this->MyStep()) {
2309
2310
                // order is being processed ...
2311
                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...
2312
            }
2313
        }
2314
        return false;
2315
    }
2316
2317
    /**
2318
     * @return bool
2319
     */
2320
    public function IsInSession()
2321
    {
2322
        $orderInSession = ShoppingCart::session_order();
2323
2324
        return $orderInSession && $this->ID && $this->ID == $orderInSession->ID;
2325
    }
2326
2327
    /**
2328
     * returns a pseudo random part of the session id.
2329
     *
2330
     * @param int $size
2331
     *
2332
     * @return string
2333
     */
2334
    public function LessSecureSessionID($size = 7, $start = null)
2335
    {
2336
        if (!$start || $start < 0 || $start > (32 - $size)) {
2337
            $start = 0;
2338
        }
2339
2340
        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...
2341
    }
2342
    /**
2343
     *
2344
     * @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...
2345
     *
2346
     * @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...
2347
     **/
2348
    public function canViewAdminStuff($member = null)
2349
    {
2350
        $member = $this->getMemberForCanFunctions($member);
2351
        $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...
2352
        if ($extended !== null) {
2353
            return $extended;
2354
        }
2355
        if (Permission::checkMember($member, Config::inst()->get('EcommerceRole', 'admin_permission_code'))) {
2356
            return true;
2357
        }
2358
    }
2359
2360
    /**
2361
     * if we set canEdit to false then we
2362
     * can not see the child records
2363
     * Basically, you can edit when you can view and canEdit (even as a customer)
2364
     * Or if you are a Shop Admin you can always edit.
2365
     * Otherwise it is false...
2366
     *
2367
     * @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...
2368
     *
2369
     * @return bool
2370
     **/
2371
    public function canEdit($member = null)
2372
    {
2373
        $member = $this->getMemberForCanFunctions($member);
2374
        $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...
2375
        if ($extended !== null) {
2376
            return $extended;
2377
        }
2378
        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...
2379
            return true;
2380
        }
2381
        if (Permission::checkMember($member, Config::inst()->get('EcommerceRole', 'admin_permission_code'))) {
2382
            return true;
2383
        }
2384
        //is the member is a shop assistant they can always view it
2385
        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...
2386
            return true;
2387
        }
2388
        return false;
2389
    }
2390
2391
    /**
2392
     * is the order ready to go through to the
2393
     * checkout process.
2394
     *
2395
     * This method checks all the order items and order modifiers
2396
     * If any of them need immediate attention then this is done
2397
     * first after which it will go through to the checkout page.
2398
     *
2399
     * @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...
2400
     *
2401
     * @return bool
2402
     **/
2403
    public function canCheckout(Member $member = null)
2404
    {
2405
        $member = $this->getMemberForCanFunctions($member);
2406
        $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...
2407
        if ($extended !== null) {
2408
            return $extended;
2409
        }
2410
        $submitErrors = $this->SubmitErrors();
2411
        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...
2412
            return false;
2413
        }
2414
2415
        return true;
2416
    }
2417
2418
    /**
2419
     * Can the order be submitted?
2420
     * this method can be used to stop an order from being submitted
2421
     * due to something not being completed or done.
2422
     *
2423
     * @see Order::SubmitErrors
2424
     *
2425
     * @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...
2426
     *
2427
     * @return bool
2428
     **/
2429
    public function canSubmit(Member $member = null)
2430
    {
2431
        $member = $this->getMemberForCanFunctions($member);
2432
        $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...
2433
        if ($extended !== null) {
2434
            return $extended;
2435
        }
2436
        if ($this->IsSubmitted()) {
2437
            return false;
2438
        }
2439
        $submitErrors = $this->SubmitErrors();
2440
        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...
2441
            return false;
2442
        }
2443
2444
        return true;
2445
    }
2446
2447
    /**
2448
     * Can a payment be made for this Order?
2449
     *
2450
     * @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...
2451
     *
2452
     * @return bool
2453
     **/
2454
    public function canPay(Member $member = null)
2455
    {
2456
        $member = $this->getMemberForCanFunctions($member);
2457
        $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...
2458
        if ($extended !== null) {
2459
            return $extended;
2460
        }
2461
        if ($this->IsPaid() || $this->IsCancelled() || $this->PaymentIsPending()) {
2462
            return false;
2463
        }
2464
2465
        return $this->MyStep()->CustomerCanPay;
2466
    }
2467
2468
    /**
2469
     * Can the given member cancel this order?
2470
     *
2471
     * @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...
2472
     *
2473
     * @return bool
2474
     **/
2475
    public function canCancel(Member $member = null)
2476
    {
2477
        //if it is already cancelled it can not be cancelled again
2478
        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...
2479
            return false;
2480
        }
2481
        $member = $this->getMemberForCanFunctions($member);
2482
        $extended = $this->extendedCan(__FUNCTION__, $member);
0 ignored issues
show
Documentation introduced by
$member is of type object<DataObject>, but the function expects a object<Member>|integer.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2483
        if ($extended !== null) {
2484
            return $extended;
2485
        }
2486
        if (EcommerceRole::current_member_can_process_orders($member)) {
2487
            return true;
2488
        }
2489
2490
        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...
2491
    }
2492
2493
    /**
2494
     * @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...
2495
     *
2496
     * @return bool
2497
     **/
2498
    public function canDelete($member = null)
2499
    {
2500
        $member = $this->getMemberForCanFunctions($member);
2501
        $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...
2502
        if ($extended !== null) {
2503
            return $extended;
2504
        }
2505
        if ($this->IsSubmitted()) {
2506
            return false;
2507
        }
2508
        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...
2509
            return true;
2510
        }
2511
2512
        return false;
2513
    }
2514
2515
    /**
2516
     * Returns all the order logs that the current member can view
2517
     * i.e. some order logs can only be viewed by the admin (e.g. suspected fraud orderlog).
2518
     *
2519
     * @return ArrayList (OrderStatusLogs)
2520
     **/
2521
    public function CanViewOrderStatusLogs()
2522
    {
2523
        $canViewOrderStatusLogs = new ArrayList();
2524
        $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...
2525
        foreach ($logs as $log) {
2526
            if ($log->canView()) {
2527
                $canViewOrderStatusLogs->push($log);
2528
            }
2529
        }
2530
2531
        return $canViewOrderStatusLogs;
2532
    }
2533
2534
    /**
2535
     * returns all the logs that can be viewed by the customer.
2536
     *
2537
     * @return ArrayList (OrderStausLogs)
2538
     */
2539
    public function CustomerViewableOrderStatusLogs()
2540
    {
2541
        $customerViewableOrderStatusLogs = new ArrayList();
2542
        $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...
2543
        if ($logs) {
2544
            foreach ($logs as $log) {
2545
                if (!$log->InternalUseOnly) {
2546
                    $customerViewableOrderStatusLogs->push($log);
2547
                }
2548
            }
2549
        }
2550
2551
        return $customerViewableOrderStatusLogs;
2552
    }
2553
2554
/*******************************************************
2555
   * 8. GET METHODS (e.g. Total, SubTotal, Title, etc...)
2556
*******************************************************/
2557
2558
    /**
2559
     * returns the email to be used for customer communication.
2560
     *
2561
     * @return string
2562
     */
2563
    public function OrderEmail()
2564
    {
2565
        return $this->getOrderEmail();
2566
    }
2567
    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...
2568
    {
2569
        $email = '';
2570
        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...
2571
            $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...
2572
        }
2573
        if (! $email) {
2574
            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...
2575
                $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...
2576
            }
2577
        }
2578
        $extendedEmail = $this->extend('updateOrderEmail', $email);
2579
        if ($extendedEmail !== null && is_array($extendedEmail) && count($extendedEmail)) {
2580
            $email = implode(';', $extendedEmail);
2581
        }
2582
2583
        return $email;
2584
    }
2585
2586
    /**
2587
     * Returns true if there is a prink or email link.
2588
     *
2589
     * @return bool
2590
     */
2591
    public function HasPrintOrEmailLink()
2592
    {
2593
        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...
2594
    }
2595
2596
    /**
2597
     * returns the absolute link to the order that can be used in the customer communication (email).
2598
     *
2599
     * @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...
2600
     */
2601
    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...
2602
    {
2603
        return $this->getEmailLink();
2604
    }
2605
    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...
2606
    {
2607
        if (!isset($_REQUEST['print'])) {
2608
            if ($this->IsSubmitted()) {
2609
                return Director::AbsoluteURL(OrderConfirmationPage::get_email_link($this->ID, $this->MyStep()->getEmailClassName(), $actuallySendEmail = true));
2610
            }
2611
        }
2612
    }
2613
2614
    /**
2615
     * returns the absolute link to the order for printing.
2616
     *
2617
     * @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...
2618
     */
2619
    public function PrintLink()
2620
    {
2621
        return $this->getPrintLink();
2622
    }
2623
    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...
2624
    {
2625
        if (!isset($_REQUEST['print'])) {
2626
            if ($this->IsSubmitted()) {
2627
                return Director::AbsoluteURL(OrderConfirmationPage::get_order_link($this->ID)).'?print=1';
2628
            }
2629
        }
2630
    }
2631
2632
    /**
2633
     * returns the absolute link to the order for printing.
2634
     *
2635
     * @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...
2636
     */
2637
    public function PackingSlipLink()
2638
    {
2639
        return $this->getPackingSlipLink();
2640
    }
2641
    public function getPackingSlipLink()
2642
    {
2643
        if ($this->IsSubmitted()) {
2644
            return Director::AbsoluteURL(OrderConfirmationPage::get_order_link($this->ID)).'?packingslip=1';
2645
        }
2646
    }
2647
2648
    /**
2649
     * returns the absolute link that the customer can use to retrieve the email WITHOUT logging in.
2650
     *
2651
     * @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...
2652
     */
2653
    public function RetrieveLink()
2654
    {
2655
        return $this->getRetrieveLink();
2656
    }
2657
2658
    public function getRetrieveLink()
2659
    {
2660
        //important to recalculate!
2661
        if ($this->IsSubmitted($recalculate = true)) {
2662
            //add session ID if not added yet...
2663
            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...
2664
                $this->write();
2665
            }
2666
2667
            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...
2668
        } else {
2669
            return Director::AbsoluteURL('/shoppingcart/loadorder/'.$this->ID.'/');
2670
        }
2671
    }
2672
2673
    public function ShareLink()
2674
    {
2675
        return $this->getShareLink();
2676
    }
2677
2678
    public function getShareLink()
2679
    {
2680
        $orderItems = $this->itemsFromDatabase();
2681
        $action = 'share';
2682
        $array = array();
2683
        foreach ($orderItems as $orderItem) {
2684
            $array[] = implode(
2685
                ',',
2686
                array(
2687
                    $orderItem->BuyableClassName,
2688
                    $orderItem->BuyableID,
2689
                    $orderItem->Quantity
2690
                )
2691
            );
2692
        }
2693
2694
        return Director::AbsoluteURL(CartPage::find_link($action.'/'.implode('-', $array)));
2695
    }
2696
2697
    /**
2698
     * @alias for getFeedbackLink
2699
     * @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...
2700
     */
2701
    public function FeedbackLink()
2702
    {
2703
        return $this->getFeedbackLink();
2704
    }
2705
2706
    /**
2707
     * @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...
2708
     */
2709
    public function getFeedbackLink()
2710
    {
2711
        $orderConfirmationPage = DataObject::get_one('OrderConfirmationPage');
2712
        if($orderConfirmationPage->IsFeedbackEnabled) {
2713
2714
            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...
2715
        }
2716
    }
2717
2718
    /**
2719
     * link to delete order.
2720
     *
2721
     * @return string
2722
     */
2723
    public function DeleteLink()
2724
    {
2725
        return $this->getDeleteLink();
2726
    }
2727
    public function getDeleteLink()
2728
    {
2729
        if ($this->canDelete()) {
2730
            return ShoppingCart_Controller::delete_order_link($this->ID);
2731
        } else {
2732
            return '';
2733
        }
2734
    }
2735
2736
    /**
2737
     * link to copy order.
2738
     *
2739
     * @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...
2740
     */
2741
    public function CopyOrderLink()
2742
    {
2743
        return $this->getCopyOrderLink();
2744
    }
2745
    public function getCopyOrderLink()
2746
    {
2747
        if ($this->canView() && $this->IsSubmitted()) {
2748
            return ShoppingCart_Controller::copy_order_link($this->ID);
2749
        } else {
2750
            return '';
2751
        }
2752
    }
2753
2754
    /**
2755
     * A "Title" for the order, which summarises the main details (date, and customer) in a string.
2756
     *
2757
     * @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...
2758
     * @param bool   $includeName - e.g. by Mr Johnson
2759
     *
2760
     * @return string
2761
     **/
2762
    public function Title($dateFormat = null, $includeName = false)
2763
    {
2764
        return $this->getTitle($dateFormat, $includeName);
2765
    }
2766
    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...
2767
    {
2768
        if ($this->exists()) {
2769
            if ($dateFormat === null) {
2770
                $dateFormat = EcommerceConfig::get('Order', 'date_format_for_title');
2771
            }
2772
            if ($includeName === null) {
2773
                $includeName = EcommerceConfig::get('Order', 'include_customer_name_in_title');
2774
            }
2775
            $title = $this->i18n_singular_name()." #".number_format($this->ID);
2776
            if ($dateFormat) {
2777
                if ($submissionLog = $this->SubmissionLog()) {
2778
                    $dateObject = $submissionLog->dbObject('Created');
2779
                    $placed = _t('Order.PLACED', 'placed');
2780
                } else {
2781
                    $dateObject = $this->dbObject('Created');
2782
                    $placed = _t('Order.STARTED', 'started');
2783
                }
2784
                $title .= ' - '.$placed.' '.$dateObject->Format($dateFormat);
2785
            }
2786
            $name = '';
2787
            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...
2788
                $name = ' - '._t('Order.CANCELLED', 'CANCELLED');
2789
            }
2790
            if ($includeName) {
2791
                $by = _t('Order.BY', 'by');
2792
                if (!$name) {
2793
                    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...
2794
                        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...
2795
                            $name = ' - '.$by.' '.$billingAddress->Prefix.' '.$billingAddress->FirstName.' '.$billingAddress->Surname;
2796
                        }
2797
                    }
2798
                }
2799
                if (!$name) {
2800
                    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...
2801
                        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...
2802
                            if ($member->exists()) {
2803
                                if ($memberName = $member->getName()) {
2804
                                    if (!trim($memberName)) {
2805
                                        $memberName = _t('Order.ANONYMOUS', 'anonymous');
2806
                                    }
2807
                                    $name = ' - '.$by.' '.$memberName;
2808
                                }
2809
                            }
2810
                        }
2811
                    }
2812
                }
2813
            }
2814
            $title .= $name;
2815
        } else {
2816
            $title = _t('Order.NEW', 'New').' '.$this->i18n_singular_name();
2817
        }
2818
        $extendedTitle = $this->extend('updateTitle', $title);
2819
        if ($extendedTitle !== null && is_array($extendedTitle) && count($extendedTitle)) {
2820
            $title = implode('; ', $extendedTitle);
2821
        }
2822
2823
        return $title;
2824
    }
2825
2826
    /**
2827
     * Returns the subtotal of the items for this order.
2828
     *
2829
     * @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...
2830
     */
2831
    public function SubTotal()
2832
    {
2833
        return $this->getSubTotal();
2834
    }
2835
    public function getSubTotal()
2836
    {
2837
        $result = 0;
2838
        $items = $this->Items();
2839
        if ($items->count()) {
2840
            foreach ($items as $item) {
2841
                if (is_a($item, Object::getCustomClass('OrderAttribute'))) {
2842
                    $result += $item->Total();
2843
                }
2844
            }
2845
        }
2846
2847
        return $result;
2848
    }
2849
2850
    /**
2851
     * @return Currency (DB Object)
2852
     **/
2853
    public function SubTotalAsCurrencyObject()
2854
    {
2855
        return DBField::create_field('Currency', $this->SubTotal());
2856
    }
2857
2858
    /**
2859
     * @return Money
2860
     **/
2861
    public function SubTotalAsMoney()
2862
    {
2863
        return $this->getSubTotalAsMoney();
2864
    }
2865
    public function getSubTotalAsMoney()
2866
    {
2867
        return EcommerceCurrency::get_money_object_from_order_currency($this->SubTotal(), $this);
2868
    }
2869
2870
    /**
2871
     * @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...
2872
     * @param bool         $stopAtExcludedModifier - when this flag is TRUE, we stop adding the modifiers when we reach an excluded modifier.
2873
     *
2874
     * @return Currency (DB Object)
2875
     **/
2876
    public function ModifiersSubTotalAsCurrencyObject($excluded = null, $stopAtExcludedModifier = false)
2877
    {
2878
        return DBField::create_field('Currency', $this->ModifiersSubTotal($excluded, $stopAtExcludedModifier));
2879
    }
2880
2881
    /**
2882
     * @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...
2883
     * @param bool         $stopAtExcludedModifier - when this flag is TRUE, we stop adding the modifiers when we reach an excluded modifier.
2884
     *
2885
     * @return Money (DB Object)
2886
     **/
2887
    public function ModifiersSubTotalAsMoneyObject($excluded = null, $stopAtExcludedModifier = false)
2888
    {
2889
        return EcommerceCurrency::get_money_object_from_order_currency($this->ModifiersSubTotal($excluded, $stopAtExcludedModifier), $this);
2890
    }
2891
2892
    /**
2893
     * Returns the total cost of an order including the additional charges or deductions of its modifiers.
2894
     *
2895
     * @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...
2896
     */
2897
    public function Total()
2898
    {
2899
        return $this->getTotal();
2900
    }
2901
    public function getTotal()
2902
    {
2903
        return $this->SubTotal() + $this->ModifiersSubTotal();
2904
    }
2905
2906
    /**
2907
     * @return Currency (DB Object)
2908
     **/
2909
    public function TotalAsCurrencyObject()
2910
    {
2911
        return DBField::create_field('Currency', $this->Total());
2912
    }
2913
2914
    /**
2915
     * @return Money
2916
     **/
2917
    public function TotalAsMoney()
2918
    {
2919
        return $this->getTotalAsMoney();
2920
    }
2921
    public function getTotalAsMoney()
2922
    {
2923
        return EcommerceCurrency::get_money_object_from_order_currency($this->Total(), $this);
2924
    }
2925
2926
    /**
2927
     * Checks to see if any payments have been made on this order
2928
     * and if so, subracts the payment amount from the order.
2929
     *
2930
     * @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...
2931
     **/
2932
    public function TotalOutstanding()
2933
    {
2934
        return $this->getTotalOutstanding();
2935
    }
2936
    public function getTotalOutstanding()
2937
    {
2938
        if ($this->IsSubmitted()) {
2939
            $total = $this->Total();
2940
            $paid = $this->TotalPaid();
2941
            $outstanding = $total - $paid;
2942
            $maxDifference = EcommerceConfig::get('Order', 'maximum_ignorable_sales_payments_difference');
2943
            if (abs($outstanding) < $maxDifference) {
2944
                $outstanding = 0;
2945
            }
2946
2947
            return floatval($outstanding);
2948
        } else {
2949
            return 0;
2950
        }
2951
    }
2952
2953
    /**
2954
     * @return Currency (DB Object)
2955
     **/
2956
    public function TotalOutstandingAsCurrencyObject()
2957
    {
2958
        return DBField::create_field('Currency', $this->TotalOutstanding());
2959
    }
2960
2961
    /**
2962
     * @return Money
2963
     **/
2964
    public function TotalOutstandingAsMoney()
2965
    {
2966
        return $this->getTotalOutstandingAsMoney();
2967
    }
2968
    public function getTotalOutstandingAsMoney()
2969
    {
2970
        return EcommerceCurrency::get_money_object_from_order_currency($this->TotalOutstanding(), $this);
2971
    }
2972
2973
    /**
2974
     * @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...
2975
     */
2976
    public function TotalPaid()
2977
    {
2978
        return $this->getTotalPaid();
2979
    }
2980
    public function getTotalPaid()
2981
    {
2982
        $paid = 0;
2983
        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...
2984
            foreach ($payments as $payment) {
2985
                if ($payment->Status == 'Success') {
2986
                    $paid += $payment->Amount->getAmount();
2987
                }
2988
            }
2989
        }
2990
        $reverseExchange = 1;
2991
        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...
2992
            $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...
2993
        }
2994
2995
        return $paid * $reverseExchange;
2996
    }
2997
2998
    /**
2999
     * @return Currency (DB Object)
3000
     **/
3001
    public function TotalPaidAsCurrencyObject()
3002
    {
3003
        return DBField::create_field('Currency', $this->TotalPaid());
3004
    }
3005
3006
    /**
3007
     * @return Money
3008
     **/
3009
    public function TotalPaidAsMoney()
3010
    {
3011
        return $this->getTotalPaidAsMoney();
3012
    }
3013
    public function getTotalPaidAsMoney()
3014
    {
3015
        return EcommerceCurrency::get_money_object_from_order_currency($this->TotalPaid(), $this);
3016
    }
3017
3018
    /**
3019
     * returns the total number of OrderItems (not modifiers).
3020
     * This is meant to run as fast as possible to quickly check
3021
     * if there is anything in the cart.
3022
     *
3023
     * @param bool $recalculate - do we need to recalculate (value is retained during lifetime of Object)
3024
     *
3025
     * @return int
3026
     **/
3027
    public function TotalItems($recalculate = false)
3028
    {
3029
        return $this->getTotalItems($recalculate);
3030
    }
3031
    public function getTotalItems($recalculate = false)
3032
    {
3033
        if ($this->totalItems === null || $recalculate) {
3034
            $this->totalItems = OrderItem::get()
3035
                ->where('"OrderAttribute"."OrderID" = '.$this->ID.' AND "OrderItem"."Quantity" > 0')
3036
                ->count();
3037
        }
3038
3039
        return $this->totalItems;
3040
    }
3041
3042
    /**
3043
     * Little shorthand.
3044
     *
3045
     * @param bool $recalculate
3046
     *
3047
     * @return bool
3048
     **/
3049
    public function MoreThanOneItemInCart($recalculate = false)
3050
    {
3051
        return $this->TotalItems($recalculate) > 1 ? true : false;
3052
    }
3053
3054
    /**
3055
     * returns the total number of OrderItems (not modifiers) times their respectective quantities.
3056
     *
3057
     * @param bool $recalculate - force recalculation
3058
     *
3059
     * @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...
3060
     **/
3061
    public function TotalItemsTimesQuantity($recalculate = false)
3062
    {
3063
        return $this->getTotalItemsTimesQuantity($recalculate);
3064
    }
3065
    public function getTotalItemsTimesQuantity($recalculate = false)
3066
    {
3067
        if ($this->totalItemsTimesQuantity === null || $recalculate) {
3068
            //to do, why do we check if you can edit ????
3069
            $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...
3070
                SELECT SUM("OrderItem"."Quantity")
3071
                FROM "OrderItem"
3072
                    INNER JOIN "OrderAttribute" ON "OrderAttribute"."ID" = "OrderItem"."ID"
3073
                WHERE
3074
                    "OrderAttribute"."OrderID" = '.$this->ID.'
3075
                    AND "OrderItem"."Quantity" > 0'
3076
            )->value();
3077
        }
3078
3079
        return $this->totalItemsTimesQuantity - 0;
3080
    }
3081
3082
    /**
3083
     *
3084
     * @return string (country code)
3085
     **/
3086
    public function Country()
3087
    {
3088
        return $this->getCountry();
3089
    }
3090
3091
    /**
3092
    * Returns the country code for the country that applies to the order.
3093
    * @alias  for getCountry
3094
    *
3095
    * @return string - country code e.g. NZ
3096
     */
3097
    public function getCountry()
3098
    {
3099
        $countryCodes = array(
3100
            'Billing' => '',
3101
            'Shipping' => '',
3102
        );
3103
        $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...
3104
        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...
3105
            $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...
3106
            if ($billingAddress) {
3107
                if ($billingAddress->Country) {
3108
                    $countryCodes['Billing'] = $billingAddress->Country;
3109
                }
3110
            }
3111
        }
3112
        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...
3113
            $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...
3114
            if ($shippingAddress) {
3115
                if ($shippingAddress->ShippingCountry) {
3116
                    $countryCodes['Shipping'] = $shippingAddress->ShippingCountry;
3117
                }
3118
            }
3119
        }
3120
        if (
3121
            (EcommerceConfig::get('OrderAddress', 'use_shipping_address_for_main_region_and_country') && $countryCodes['Shipping'])
3122
            ||
3123
            (!$countryCodes['Billing'] && $countryCodes['Shipping'])
3124
        ) {
3125
            $code = $countryCodes['Shipping'];
3126
        } elseif ($countryCodes['Billing']) {
3127
            $code = $countryCodes['Billing'];
3128
        } else {
3129
            $code = EcommerceCountry::get_country_from_ip();
3130
        }
3131
3132
        return $code;
3133
    }
3134
3135
    /**
3136
     * @alias for getFullNameCountry
3137
     *
3138
     * @return string - country name
3139
     **/
3140
    public function FullNameCountry()
3141
    {
3142
        return $this->getFullNameCountry();
3143
    }
3144
3145
    /**
3146
     * returns name of coutry.
3147
     *
3148
     * @return string - country name
3149
     **/
3150
    public function getFullNameCountry()
3151
    {
3152
        return EcommerceCountry::find_title($this->Country());
3153
    }
3154
3155
    /**
3156
     * @alis for getExpectedCountryName
3157
     * @return string - country name
3158
     **/
3159
    public function ExpectedCountryName()
3160
    {
3161
        return $this->getExpectedCountryName();
3162
    }
3163
3164
    /**
3165
     * returns name of coutry that we expect the customer to have
3166
     * This takes into consideration more than just what has been entered
3167
     * for example, it looks at GEO IP.
3168
     *
3169
     * @todo: why do we dont return a string IF there is only one item.
3170
     *
3171
     * @return string - country name
3172
     **/
3173
    public function getExpectedCountryName()
3174
    {
3175
        return EcommerceCountry::find_title(EcommerceCountry::get_country(false, $this->ID));
3176
    }
3177
3178
    /**
3179
     * return the title of the fixed country (if any).
3180
     *
3181
     * @return string | empty string
3182
     **/
3183
    public function FixedCountry()
3184
    {
3185
        return $this->getFixedCountry();
3186
    }
3187
    public function getFixedCountry()
3188
    {
3189
        $code = EcommerceCountry::get_fixed_country_code();
3190
        if ($code) {
3191
            return EcommerceCountry::find_title($code);
3192
        }
3193
3194
        return '';
3195
    }
3196
3197
    /**
3198
     * Returns the region that applies to the order.
3199
     * we check both billing and shipping, in case one of them is empty.
3200
     *
3201
     * @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...
3202
     **/
3203
    public function Region()
3204
    {
3205
        return $this->getRegion();
3206
    }
3207
    public function getRegion()
3208
    {
3209
        $regionIDs = array(
3210
            'Billing' => 0,
3211
            'Shipping' => 0,
3212
        );
3213
        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...
3214
            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...
3215
                if ($billingAddress->RegionID) {
3216
                    $regionIDs['Billing'] = $billingAddress->RegionID;
3217
                }
3218
            }
3219
        }
3220
        if ($this->CanHaveShippingAddress()) {
3221
            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...
3222
                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...
3223
                    if ($shippingAddress->ShippingRegionID) {
3224
                        $regionIDs['Shipping'] = $shippingAddress->ShippingRegionID;
3225
                    }
3226
                }
3227
            }
3228
        }
3229
        if (count($regionIDs)) {
3230
            //note the double-check with $this->CanHaveShippingAddress() and get_use_....
3231
            if ($this->CanHaveShippingAddress() && EcommerceConfig::get('OrderAddress', 'use_shipping_address_for_main_region_and_country') && $regionIDs['Shipping']) {
3232
                return EcommerceRegion::get()->byID($regionIDs['Shipping']);
3233
            } else {
3234
                return EcommerceRegion::get()->byID($regionIDs['Billing']);
3235
            }
3236
        } else {
3237
            return EcommerceRegion::get()->byID(EcommerceRegion::get_region_from_ip());
3238
        }
3239
    }
3240
3241
    /**
3242
     * Casted variable
3243
     * Currency is not the same as the standard one?
3244
     *
3245
     * @return bool
3246
     **/
3247
    public function HasAlternativeCurrency()
3248
    {
3249
        return $this->getHasAlternativeCurrency();
3250
    }
3251
    public function getHasAlternativeCurrency()
3252
    {
3253
        if ($currency = $this->CurrencyUsed()) {
0 ignored issues
show
Documentation Bug introduced by
The method CurrencyUsed does not exist on object<Order>? Since you implemented __call, maybe consider adding a @method annotation.

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

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

class ParentClass {
    private $data = array();

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

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

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
3254
            if ($currency->IsDefault()) {
0 ignored issues
show
Coding Style introduced by
The if-else statement can be simplified to return !$currency->IsDefault();.
Loading history...
3255
                return false;
3256
            } else {
3257
                return true;
3258
            }
3259
        } else {
3260
            return false;
3261
        }
3262
    }
3263
3264
    /**
3265
     * Makes sure exchange rate is updated and maintained before order is submitted
3266
     * This method is public because it could be called from a shopping Cart Object.
3267
     **/
3268
    public function EnsureCorrectExchangeRate()
3269
    {
3270
        if (!$this->IsSubmitted()) {
3271
            $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...
3272
            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...
3273
                if ($currency->IsDefault()) {
3274
                    $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...
3275
                } else {
3276
                    $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...
3277
                }
3278
            } else {
3279
                $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...
3280
            }
3281
            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...
3282
                $this->write();
3283
            }
3284
        }
3285
    }
3286
3287
    /**
3288
     * speeds up processing by storing the IsSubmitted value
3289
     * we start with -1 to know if it has been requested before.
3290
     *
3291
     * @var bool
3292
     */
3293
    protected $_isSubmittedTempVar = -1;
3294
3295
    /**
3296
     * Casted variable - has the order been submitted?
3297
     * alias
3298
     * @param bool $recalculate
3299
     *
3300
     * @return bool
3301
     **/
3302
    public function IsSubmitted($recalculate = true)
3303
    {
3304
        return $this->getIsSubmitted($recalculate);
3305
    }
3306
3307
    /**
3308
     * Casted variable - has the order been submitted?
3309
     *
3310
     * @param bool $recalculate
3311
     *
3312
     * @return bool
3313
     **/
3314
    public function getIsSubmitted($recalculate = false)
3315
    {
3316
        if ($this->_isSubmittedTempVar === -1 || $recalculate) {
3317
            if ($this->SubmissionLog()) {
3318
                $this->_isSubmittedTempVar = true;
3319
            } else {
3320
                $this->_isSubmittedTempVar = false;
3321
            }
3322
        }
3323
3324
        return $this->_isSubmittedTempVar;
3325
    }
3326
3327
    /**
3328
     *
3329
     *
3330
     * @return bool
3331
     */
3332
    public function IsArchived()
3333
    {
3334
        $lastStep = OrderStep::last_order_step();
3335
        if ($lastStep) {
3336
            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...
3337
                return true;
3338
            }
3339
        }
3340
        return false;
3341
    }
3342
3343
    /**
3344
     * Submission Log for this Order (if any).
3345
     *
3346
     * @return Submission Log (OrderStatusLog_Submitted) | Null
3347
     **/
3348
    public function SubmissionLog()
3349
    {
3350
        $className = EcommerceConfig::get('OrderStatusLog', 'order_status_log_class_used_for_submitting_order');
3351
3352
        return $className::get()
3353
            ->Filter(array('OrderID' => $this->ID))
3354
            ->Last();
3355
    }
3356
3357
    /**
3358
     * Submission Log for this Order (if any).
3359
     *
3360
     * @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...
3361
     **/
3362
    public function OrderDate()
3363
    {
3364
        $object = $this->SubmissionLog();
3365
        if($object) {
3366
            $created = $object->Created;
3367
        } else {
3368
            $created = $this->LastEdited;
3369
        }
3370
3371
        return DBField::create_field('SS_Datetime', $created);
3372
    }
3373
3374
    /**
3375
     * @return int
3376
     */
3377
    public function SecondsSinceBeingSubmitted()
3378
    {
3379
        if ($submissionLog = $this->SubmissionLog()) {
3380
            return time() - strtotime($submissionLog->Created);
3381
        } else {
3382
            return 0;
3383
        }
3384
    }
3385
3386
    /**
3387
     * if the order can not be submitted,
3388
     * then the reasons why it can not be submitted
3389
     * will be returned by this method.
3390
     *
3391
     * @see Order::canSubmit
3392
     *
3393
     * @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...
3394
     */
3395
    public function SubmitErrors()
3396
    {
3397
        $al = null;
3398
        $extendedSubmitErrors = $this->extend('updateSubmitErrors');
3399
        if ($extendedSubmitErrors !== null && is_array($extendedSubmitErrors) && count($extendedSubmitErrors)) {
3400
            $al = ArrayList::create();
3401
            foreach ($extendedSubmitErrors as $returnResultArray) {
3402
                foreach ($returnResultArray as $issue) {
3403
                    if ($issue) {
3404
                        $al->push(ArrayData::create(array("Title" => $issue)));
3405
                    }
3406
                }
3407
            }
3408
        }
3409
        return $al;
3410
    }
3411
3412
    /**
3413
     * Casted variable - has the order been submitted?
3414
     *
3415
     * @param bool $withDetail
3416
     *
3417
     * @return string
3418
     **/
3419
    public function CustomerStatus($withDetail = true)
3420
    {
3421
        return $this->getCustomerStatus($withDetail);
3422
    }
3423
    public function getCustomerStatus($withDetail = true)
3424
    {
3425
        $str = '';
3426
        if ($this->MyStep()->ShowAsUncompletedOrder) {
3427
            $str = _t('Order.UNCOMPLETED', 'Uncompleted');
3428
        } elseif ($this->MyStep()->ShowAsInProcessOrder) {
3429
            $str = _t('Order.IN_PROCESS', 'In Process');
3430
        } elseif ($this->MyStep()->ShowAsCompletedOrder) {
3431
            $str = _t('Order.COMPLETED', 'Completed');
3432
        }
3433
        if ($withDetail) {
3434
            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...
3435
                $str .= ' ('.$this->MyStep()->Name.')';
3436
            }
3437
        }
3438
3439
        return $str;
3440
    }
3441
3442
    /**
3443
     * Casted variable - does the order have a potential shipping address?
3444
     *
3445
     * @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...
3446
     **/
3447
    public function CanHaveShippingAddress()
3448
    {
3449
        return $this->getCanHaveShippingAddress();
3450
    }
3451
    public function getCanHaveShippingAddress()
3452
    {
3453
        return EcommerceConfig::get('OrderAddress', 'use_separate_shipping_address');
3454
    }
3455
3456
    /**
3457
     * returns the link to view the Order
3458
     * WHY NOT CHECKOUT PAGE: first we check for cart page.
3459
     *
3460
     * @return CartPage | Null
3461
     */
3462
    public function DisplayPage()
3463
    {
3464
        if ($this->MyStep() && $this->MyStep()->AlternativeDisplayPage()) {
3465
            $page = $this->MyStep()->AlternativeDisplayPage();
3466
        } elseif ($this->IsSubmitted()) {
3467
            $page = DataObject::get_one('OrderConfirmationPage');
3468
        } else {
3469
            $page = DataObject::get_one(
3470
                'CartPage',
3471
                array('ClassName' => 'CartPage')
3472
            );
3473
            if (!$page) {
3474
                $page = DataObject::get_one('CheckoutPage');
3475
            }
3476
        }
3477
3478
        return $page;
3479
    }
3480
3481
    /**
3482
     * returns the link to view the Order
3483
     * WHY NOT CHECKOUT PAGE: first we check for cart page.
3484
     * If a cart page has been created then we refer through to Cart Page.
3485
     * Otherwise it will default to the checkout page.
3486
     *
3487
     * @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...
3488
     *
3489
     * @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...
3490
     */
3491
    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...
3492
    {
3493
        $page = $this->DisplayPage();
3494
        if ($page) {
3495
            return $page->getOrderLink($this->ID, $action);
3496
        } else {
3497
            user_error('A Cart / Checkout Page + an Order Confirmation Page needs to be setup for the e-commerce module to work.', E_USER_NOTICE);
3498
            $page = DataObject::get_one(
3499
                'ErrorPage',
3500
                array('ErrorCode' => '404')
3501
            );
3502
            if ($page) {
3503
                return $page->Link();
3504
            }
3505
        }
3506
    }
3507
3508
    /**
3509
     * Returns to link to access the Order's API.
3510
     *
3511
     * @param string $version
3512
     * @param string $extension
3513
     *
3514
     * @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...
3515
     */
3516
    public function APILink($version = 'v1', $extension = 'xml')
3517
    {
3518
        return Director::AbsoluteURL("/api/ecommerce/$version/Order/".$this->ID."/.$extension");
3519
    }
3520
3521
    /**
3522
     * returns the link to finalise the Order.
3523
     *
3524
     * @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...
3525
     */
3526
    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...
3527
    {
3528
        $page = DataObject::get_one('CheckoutPage');
3529
        if ($page) {
3530
            return $page->Link();
3531
        } else {
3532
            $page = DataObject::get_one(
3533
                'ErrorPage',
3534
                array('ErrorCode' => '404')
3535
            );
3536
            if ($page) {
3537
                return $page->Link();
3538
            }
3539
        }
3540
    }
3541
3542
    /**
3543
     * Converts the Order into HTML, based on the Order Template.
3544
     *
3545
     * @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...
3546
     **/
3547
    public function ConvertToHTML()
3548
    {
3549
        Config::nest();
3550
        Config::inst()->update('SSViewer', 'theme_enabled', true);
3551
        $html = $this->renderWith('Order');
3552
        Config::unnest();
3553
        $html = preg_replace('/(\s)+/', ' ', $html);
3554
3555
        return DBField::create_field('HTMLText', $html);
3556
    }
3557
3558
    /**
3559
     * Converts the Order into a serialized string
3560
     * TO DO: check if this works and check if we need to use special sapphire serialization code.
3561
     *
3562
     * @return string - serialized object
3563
     **/
3564
    public function ConvertToString()
3565
    {
3566
        return serialize($this->addHasOneAndHasManyAsVariables());
3567
    }
3568
3569
    /**
3570
     * Converts the Order into a JSON object
3571
     * TO DO: check if this works and check if we need to use special sapphire JSON code.
3572
     *
3573
     * @return string -  JSON
3574
     **/
3575
    public function ConvertToJSON()
3576
    {
3577
        return json_encode($this->addHasOneAndHasManyAsVariables());
3578
    }
3579
3580
    /**
3581
     * returns itself wtih more data added as variables.
3582
     * We add has_one and has_many as variables like this: $this->MyHasOne_serialized = serialize($this->MyHasOne()).
3583
     *
3584
     * @return Order - with most important has one and has many items included as variables.
3585
     **/
3586
    protected function addHasOneAndHasManyAsVariables()
3587
    {
3588
        $object = clone $this;
3589
        $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...
3590
        $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...
3591
        $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...
3592
        $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...
3593
        $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...
3594
        $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...
3595
        $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...
3596
3597
        return $object;
3598
    }
3599
3600
/*******************************************************
3601
   * 9. TEMPLATE RELATED STUFF
3602
*******************************************************/
3603
3604
    /**
3605
     * returns the instance of EcommerceConfigAjax for use in templates.
3606
     * In templates, it is used like this:
3607
     * $EcommerceConfigAjax.TableID.
3608
     *
3609
     * @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...
3610
     **/
3611
    public function AJAXDefinitions()
3612
    {
3613
        return EcommerceConfigAjax::get_one($this);
3614
    }
3615
3616
    /**
3617
     * returns the instance of EcommerceDBConfig.
3618
     *
3619
     * @return EcommerceDBConfig
3620
     **/
3621
    public function EcomConfig()
3622
    {
3623
        return EcommerceDBConfig::current_ecommerce_db_config();
3624
    }
3625
3626
    /**
3627
     * Collects the JSON data for an ajax return of the cart.
3628
     *
3629
     * @param array $js
3630
     *
3631
     * @return array (for use in AJAX for JSON)
3632
     **/
3633
    public function updateForAjax(array $js)
3634
    {
3635
        $function = EcommerceConfig::get('Order', 'ajax_subtotal_format');
3636
        if (is_array($function)) {
3637
            list($function, $format) = $function;
3638
        }
3639
        $subTotal = $this->$function();
3640
        if (isset($format)) {
3641
            $subTotal = $subTotal->$format();
3642
            unset($format);
3643
        }
3644
        $function = EcommerceConfig::get('Order', 'ajax_total_format');
3645
        if (is_array($function)) {
3646
            list($function, $format) = $function;
3647
        }
3648
        $total = $this->$function();
3649
        if (isset($format)) {
3650
            $total = $total->$format();
3651
        }
3652
        $ajaxObject = $this->AJAXDefinitions();
3653
        $js[] = array(
3654
            't' => 'id',
3655
            's' => $ajaxObject->TableSubTotalID(),
3656
            'p' => 'innerHTML',
3657
            'v' => $subTotal,
3658
        );
3659
        $js[] = array(
3660
            't' => 'id',
3661
            's' => $ajaxObject->TableTotalID(),
3662
            'p' => 'innerHTML',
3663
            'v' => $total,
3664
        );
3665
        $js[] = array(
3666
            't' => 'class',
3667
            's' => $ajaxObject->TotalItemsClassName(),
3668
            'p' => 'innerHTML',
3669
            'v' => $this->TotalItems($recalculate = true),
3670
        );
3671
        $js[] = array(
3672
            't' => 'class',
3673
            's' => $ajaxObject->TotalItemsTimesQuantityClassName(),
3674
            'p' => 'innerHTML',
3675
            'v' => $this->TotalItemsTimesQuantity(),
3676
        );
3677
        $js[] = array(
3678
            't' => 'class',
3679
            's' => $ajaxObject->ExpectedCountryClassName(),
3680
            'p' => 'innerHTML',
3681
            'v' => $this->ExpectedCountryName(),
3682
        );
3683
3684
        return $js;
3685
    }
3686
3687
    /**
3688
     * @ToDO: move to more appropriate class
3689
     *
3690
     * @return float
3691
     **/
3692
    public function SubTotalCartValue()
3693
    {
3694
        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...
3695
    }
3696
3697
/*******************************************************
3698
   * 10. STANDARD SS METHODS (requireDefaultRecords, onBeforeDelete, etc...)
3699
*******************************************************/
3700
3701
    /**
3702
     *standard SS method.
3703
     **/
3704
    public function populateDefaults()
3705
    {
3706
        parent::populateDefaults();
3707
    }
3708
3709
    public function onBeforeWrite()
3710
    {
3711
        parent::onBeforeWrite();
3712
        if (! $this->getCanHaveShippingAddress()) {
3713
            $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...
3714
        }
3715
        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...
3716
            $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...
3717
        }
3718
        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...
3719
            $generator = Injector::inst()->create('RandomGenerator');
3720
            $token = $generator->randomToken('sha1');
3721
            $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...
3722
        }
3723
    }
3724
3725
    /**
3726
     * standard SS method
3727
     * adds the ability to update order after writing it.
3728
     **/
3729
    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...
3730
    {
3731
        parent::onAfterWrite();
3732
        //crucial!
3733
        self::set_needs_recalculating(true, $this->ID);
3734
        // quick double-check
3735
        if ($this->IsCancelled() && ! $this->IsArchived()) {
3736
            $this->Archive($avoidWrites = true);
3737
        }
3738
        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...
3739
            //do nothing
3740
        } else {
3741
            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...
3742
                $this->calculateOrderAttributes($recalculate = false);
3743
                if (EcommerceRole::current_member_is_shop_admin()) {
3744
                    if (isset($_REQUEST['SubmitOrderViaCMS'])) {
3745
                        $this->tryToFinaliseOrder();
3746
                        //just in case it writes again...
3747
                        unset($_REQUEST['SubmitOrderViaCMS']);
3748
                    }
3749
                }
3750
            }
3751
        }
3752
    }
3753
3754
    /**
3755
     *standard SS method.
3756
     *
3757
     * delete attributes, statuslogs, and payments
3758
     * THIS SHOULD NOT BE USED AS ORDERS SHOULD BE CANCELLED NOT DELETED
3759
     */
3760
    public function onBeforeDelete()
3761
    {
3762
        parent::onBeforeDelete();
3763
        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...
3764
            foreach ($attributes as $attribute) {
3765
                $attribute->delete();
3766
                $attribute->destroy();
3767
            }
3768
        }
3769
3770
        //THE REST WAS GIVING ERRORS - POSSIBLY DUE TO THE FUNNY RELATIONSHIP (one-one, two times...)
3771
        /*
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...
3772
        if($billingAddress = $this->BillingAddress()) {
3773
            if($billingAddress->exists()) {
3774
                $billingAddress->delete();
3775
                $billingAddress->destroy();
3776
            }
3777
        }
3778
        if($shippingAddress = $this->ShippingAddress()) {
3779
            if($shippingAddress->exists()) {
3780
                $shippingAddress->delete();
3781
                $shippingAddress->destroy();
3782
            }
3783
        }
3784
3785
        if($statuslogs = $this->OrderStatusLogs()){
3786
            foreach($statuslogs as $log){
3787
                $log->delete();
3788
                $log->destroy();
3789
            }
3790
        }
3791
        if($payments = $this->Payments()){
3792
            foreach($payments as $payment){
3793
                $payment->delete();
3794
                $payment->destroy();
3795
            }
3796
        }
3797
        if($emails = $this->Emails()) {
3798
            foreach($emails as $email){
3799
                $email->delete();
3800
                $email->destroy();
3801
            }
3802
        }
3803
        */
3804
    }
3805
3806
/*******************************************************
3807
   * 11. DEBUG
3808
*******************************************************/
3809
3810
    /**
3811
     * Debug helper method.
3812
     * Can be called from /shoppingcart/debug/.
3813
     *
3814
     * @return string
3815
     */
3816
    public function debug()
3817
    {
3818
        $this->calculateOrderAttributes(true);
3819
3820
        return EcommerceTaskDebugCart::debug_object($this);
3821
    }
3822
}
3823