Completed
Push — master ( 613b2e...a2195f )
by Nicolaas
03:28
created

Order::RetrieveModifier()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
dl 0
loc 11
rs 9.2
c 0
b 0
f 0
eloc 6
nc 4
nop 1
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
            'Title',
46
            'Total',
47
            'SubTotal',
48
            'TotalPaid',
49
            'TotalOutstanding',
50
            'ExchangeRate',
51
            'CurrencyUsed',
52
            'TotalItems',
53
            'TotalItemsTimesQuantity',
54
            'IsCancelled',
55
            'Country',
56
            'FullNameCountry',
57
            'IsSubmitted',
58
            'CustomerStatus',
59
            'CanHaveShippingAddress',
60
            'CancelledBy',
61
            'CurrencyUsed',
62
            'BillingAddress',
63
            'UseShippingAddress',
64
            'ShippingAddress',
65
            'Status',
66
            'Attributes',
67
            'OrderStatusLogs',
68
            'MemberID',
69
        ),
70
    );
71
72
    /**
73
     * standard SS variable.
74
     *
75
     * @var array
76
     */
77
    private static $db = array(
0 ignored issues
show
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...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
78
        '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
79
        'UseShippingAddress' => 'Boolean',
80
        'CustomerOrderNote' => 'Text',
81
        'ExchangeRate' => 'Double',
82
        //'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...
83
        //'TotalItemsTimesQuantity_Saved' => 'Double'
84
    );
85
86
    private static $has_one = array(
0 ignored issues
show
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...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
87
        'Member' => 'Member',
88
        'BillingAddress' => 'BillingAddress',
89
        'ShippingAddress' => 'ShippingAddress',
90
        'Status' => 'OrderStep',
91
        'CancelledBy' => 'Member',
92
        'CurrencyUsed' => 'EcommerceCurrency',
93
    );
94
95
    /**
96
     * standard SS variable.
97
     *
98
     * @var array
99
     */
100
    private static $has_many = array(
0 ignored issues
show
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...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
101
        'Attributes' => 'OrderAttribute',
102
        'OrderStatusLogs' => 'OrderStatusLog',
103
        'Payments' => 'EcommercePayment',
104
        'Emails' => 'OrderEmailRecord',
105
    );
106
107
    /**
108
     * standard SS variable.
109
     *
110
     * @var array
111
     */
112
    private static $indexes = array(
0 ignored issues
show
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...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
113
        'SessionID' => true,
114
    );
115
116
    /**
117
     * standard SS variable.
118
     *
119
     * @var string
120
     */
121
    private static $default_sort = '"LastEdited" DESC';
0 ignored issues
show
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...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
122
123
    /**
124
     * standard SS variable.
125
     *
126
     * @var array
127
     */
128
    private static $casting = array(
0 ignored issues
show
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...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
129
        'OrderEmail' => 'Text',
130
        'EmailLink' => 'Text',
131
        'PrintLink' => 'Text',
132
        'ShareLink' => 'Text',
133
        'RetrieveLink' => 'Text',
134
        'Title' => 'Text',
135
        'Total' => 'Currency',
136
        'TotalAsMoney' => 'Money',
137
        'SubTotal' => 'Currency',
138
        'SubTotalAsMoney' => 'Money',
139
        'TotalPaid' => 'Currency',
140
        'TotalPaidAsMoney' => 'Money',
141
        'TotalOutstanding' => 'Currency',
142
        'TotalOutstandingAsMoney' => 'Money',
143
        'HasAlternativeCurrency' => 'Boolean',
144
        'TotalItems' => 'Double',
145
        'TotalItemsTimesQuantity' => 'Double',
146
        'IsCancelled' => 'Boolean',
147
        'Country' => 'Varchar(3)', //This is the applicable country for the order - for tax purposes, etc....
148
        'FullNameCountry' => 'Varchar',
149
        'IsSubmitted' => 'Boolean',
150
        'CustomerStatus' => 'Varchar',
151
        'CanHaveShippingAddress' => 'Boolean',
152
    );
153
154
    /**
155
     * standard SS variable.
156
     *
157
     * @var string
158
     */
159
    private static $singular_name = 'Order';
0 ignored issues
show
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...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
160
    public function i18n_singular_name()
161
    {
162
        return _t('Order.ORDER', 'Order');
163
    }
164
165
    /**
166
     * standard SS variable.
167
     *
168
     * @var string
169
     */
170
    private static $plural_name = 'Orders';
0 ignored issues
show
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...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
171
    public function i18n_plural_name()
172
    {
173
        return _t('Order.ORDERS', 'Orders');
174
    }
175
176
    /**
177
     * Standard SS variable.
178
     *
179
     * @var string
180
     */
181
    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...
182
183
    /**
184
     * Tells us if an order needs to be recalculated
185
     * can save one for each order...
186
     *
187
     * @var array
188
     */
189
    private static $_needs_recalculating = array();
190
191
    /**
192
     * @param bool (optional) $b
193
     * @param int (optional)  $orderID
194
     *
195
     * @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...
196
     */
197
    public static function set_needs_recalculating($b = true, $orderID = 0)
198
    {
199
        self::$_needs_recalculating[$orderID] = $b;
200
    }
201
202
    /**
203
     * @param int (optional) $orderID
204
     *
205
     * @return bool
206
     */
207
    public static function get_needs_recalculating($orderID = 0)
208
    {
209
        return isset(self::$_needs_recalculating[$orderID]) ? self::$_needs_recalculating[$orderID] : false;
210
    }
211
212
    /**
213
     * Total Items : total items in cart
214
     * We start with -1 to easily identify if it has been run before.
215
     *
216
     * @var int
217
     */
218
    protected $totalItems = null;
219
220
    /**
221
     * Total Items : total items in cart
222
     * We start with -1 to easily identify if it has been run before.
223
     *
224
     * @var float
225
     */
226
    protected $totalItemsTimesQuantity = null;
227
228
    /**
229
     * Returns a set of modifier forms for use in the checkout order form,
230
     * Controller is optional, because the orderForm has its own default controller.
231
     *
232
     * This method only returns the Forms that should be included outside
233
     * the editable table... Forms within it can be called
234
     * from through the modifier itself.
235
     *
236
     * @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...
237
     * @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...
238
     *
239
     * @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...
240
     **/
241
    public function getModifierForms(Controller $optionalController = null, Validator $optionalValidator = null)
242
    {
243
        $arrayList = new ArrayList();
244
        $modifiers = $this->Modifiers();
245
        if ($modifiers->count()) {
246
            foreach ($modifiers as $modifier) {
247
                if ($modifier->ShowForm()) {
248
                    if ($form = $modifier->getModifierForm($optionalController, $optionalValidator)) {
249
                        $form->ShowFormInEditableOrderTable = $modifier->ShowFormInEditableOrderTable();
250
                        $form->ShowFormOutsideEditableOrderTable = $modifier->ShowFormOutsideEditableOrderTable();
251
                        $form->ModifierName = $modifier->ClassName;
252
                        $arrayList->push($form);
253
                    }
254
                }
255
            }
256
        }
257
        if ($arrayList->count()) {
258
            return $arrayList;
259
        } else {
260
            return;
261
        }
262
    }
263
264
    /**
265
     * This function returns the OrderSteps.
266
     *
267
     * @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...
268
     **/
269
    public static function get_order_status_options()
270
    {
271
        return OrderStep::get();
272
    }
273
274
    /**
275
     * Like the standard byID, but it checks whether we are allowed to view the order.
276
     *
277
     * @return: Order | Null
278
     **/
279
    public static function get_by_id_if_can_view($id)
280
    {
281
        $order = Order::get()->byID($id);
282
        if ($order && $order->canView()) {
283
            if ($order->IsSubmitted()) {
284
                // LITTLE HACK TO MAKE SURE WE SHOW THE LATEST INFORMATION!
285
                $order->tryToFinaliseOrder();
286
            }
287
288
            return $order;
289
        }
290
291
        return;
292
    }
293
294
    /**
295
     * returns a Datalist with the submitted order log included
296
     * this allows you to sort the orders by their submit dates.
297
     * You can retrieve this list and then add more to it (e.g. additional filters, additional joins, etc...).
298
     *
299
     * @param bool $onlySubmittedOrders - only include Orders that have already been submitted.
300
     * @param bool $includeCancelledOrders - only include Orders that have already been submitted.
301
     *
302
     * @return DataList (Orders)
303
     */
304
    public static function get_datalist_of_orders_with_submit_record($onlySubmittedOrders = true, $includeCancelledOrders = false)
305
    {
306
        if ($onlySubmittedOrders) {
307
            $submittedOrderStatusLogClassName = EcommerceConfig::get('OrderStatusLog', 'order_status_log_class_used_for_submitting_order');
308
            $list = Order::get()
309
                ->LeftJoin('OrderStatusLog', '"Order"."ID" = "OrderStatusLog"."OrderID"')
310
                ->LeftJoin($submittedOrderStatusLogClassName, '"OrderStatusLog"."ID" = "'.$submittedOrderStatusLogClassName.'"."ID"')
311
                ->Sort('OrderStatusLog.Created', 'ASC');
312
            $where = ' ("OrderStatusLog"."ClassName" = \''.$submittedOrderStatusLogClassName.'\') ';
313
        } else {
314
            $list = Order::get();
315
            $where = ' ("StatusID" > 0) ';
316
        }
317
        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...
318
            //do nothing...
319
        } else {
320
            $where .= ' AND ("CancelledByID" = 0 OR "CancelledByID" IS NULL)';
321
        }
322
        $list = $list->where($where);
323
324
        return $list;
325
    }
326
327
/*******************************************************
328
   * 1. CMS STUFF
329
*******************************************************/
330
331
    /**
332
     * fields that we remove from the parent::getCMSFields object set.
333
     *
334
     * @var array
335
     */
336
    protected $fieldsAndTabsToBeRemoved = array(
337
        'MemberID',
338
        'Attributes',
339
        'SessionID',
340
        'Emails',
341
        'BillingAddressID',
342
        'ShippingAddressID',
343
        'UseShippingAddress',
344
        'OrderStatusLogs',
345
        'Payments',
346
        'OrderDate',
347
        'ExchangeRate',
348
        'CurrencyUsedID',
349
        'StatusID',
350
        'Currency',
351
    );
352
353
    /**
354
     * STANDARD SILVERSTRIPE STUFF.
355
     **/
356
    private static $summary_fields = array(
0 ignored issues
show
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...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
357
        'Title' => 'Title',
358
        'Status.Title' => 'Next Step',
359
        'Member.Surname' => 'Name',
360
        'Member.Email' => 'Email',
361
        'TotalAsMoney.Nice' => 'Total',
362
        'TotalItemsTimesQuantity' => 'Units'
363
    );
364
365
    /**
366
     * STANDARD SILVERSTRIPE STUFF.
367
     *
368
     * @todo: how to translate this?
369
     **/
370
    private static $searchable_fields = array(
0 ignored issues
show
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...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
371
        'ID' => array(
372
            'field' => 'NumericField',
373
            'title' => 'Order Number',
374
        ),
375
        'MemberID' => array(
376
            'field' => 'TextField',
377
            'filter' => 'OrderFilters_MemberAndAddress',
378
            'title' => 'Customer Details',
379
        ),
380
        'Created' => array(
381
            'field' => 'TextField',
382
            'filter' => 'OrderFilters_AroundDateFilter',
383
            'title' => 'Date (e.g. Today, 1 jan 2007, or last week)',
384
        ),
385
        //make sure to keep the items below, otherwise they do not show in form
386
        'StatusID' => array(
387
            'filter' => 'OrderFilters_MultiOptionsetStatusIDFilter',
388
        ),
389
        'CancelledByID' => array(
390
            'filter' => 'OrderFilters_HasBeenCancelled',
391
            'title' => 'Cancelled by ...',
392
        ),
393
    );
394
395
    /**
396
     * Determine which properties on the DataObject are
397
     * searchable, and map them to their default {@link FormField}
398
     * representations. Used for scaffolding a searchform for {@link ModelAdmin}.
399
     *
400
     * Some additional logic is included for switching field labels, based on
401
     * how generic or specific the field type is.
402
     *
403
     * Used by {@link SearchContext}.
404
     *
405
     * @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...
406
     *                       'fieldClasses': Associative array of field names as keys and FormField classes as values
407
     *                       'restrictFields': Numeric array of a field name whitelist
408
     *
409
     * @return FieldList
410
     */
411
    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...
412
    {
413
        $fieldList = parent::scaffoldSearchFields($_params);
414
        $statusOptions = OrderStep::get();
415
        if ($statusOptions && $statusOptions->count()) {
416
            $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...
417
            $preSelected = array();
418
            $createdOrderStatus = $statusOptions->First();
419
            if ($createdOrderStatus) {
420
                $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...
421
            }
422
            $arrayOfStatusOptions = clone $statusOptions->map('ID', 'Title');
423
            $arrayOfStatusOptionsFinal = array();
424
            if (count($arrayOfStatusOptions)) {
425
                foreach ($arrayOfStatusOptions as $key => $value) {
426
                    if (isset($_GET['q']['StatusID'][$key])) {
427
                        $preSelected[$key] = $key;
428
                    }
429
                    $count = Order::get()
430
                        ->Filter(array('StatusID' => intval($key)))
431
                        ->count();
432
                    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...
433
                        //do nothing
434
                    } else {
435
                        $arrayOfStatusOptionsFinal[$key] = $value." ($count)";
436
                    }
437
                }
438
            }
439
            $statusField = new CheckboxSetField(
440
                'StatusID',
441
                Injector::inst()->get('OrderStep')->i18n_singular_name(),
442
                $arrayOfStatusOptionsFinal,
443
                $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...
444
            );
445
            $fieldList->push($statusField);
446
        }
447
        $fieldList->push(new DropdownField('CancelledByID', 'Cancelled', array(-1 => '(Any)', 1 => 'yes', 0 => 'no')));
448
449
        return $fieldList;
450
    }
451
452
    /**
453
     * link to edit the record.
454
     *
455
     * @param string | Null $action - e.g. edit
456
     *
457
     * @return string
458
     */
459
    public function CMSEditLink($action = null)
460
    {
461
        return Controller::join_links(
462
            Director::baseURL(),
463
            '/admin/sales/'.$this->ClassName.'/EditForm/field/'.$this->ClassName.'/item/'.$this->ID.'/',
464
            $action
465
        );
466
    }
467
468
    /**
469
     * STANDARD SILVERSTRIPE STUFF
470
     * broken up into submitted and not (yet) submitted.
471
     **/
472
    public function getCMSFields()
0 ignored issues
show
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...
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...
473
    {
474
        $fields = $this->scaffoldFormFields(array(
475
            // Don't allow has_many/many_many relationship editing before the record is first saved
476
            'includeRelations' => false,
477
            'tabbed' => true,
478
            'ajaxSafe' => true
479
        ));
480
        $fields->insertBefore(
481
            Tab::create(
482
                'Next',
483
                _t('Order.NEXT_TAB', 'Action')
484
            ),
485
            '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...
486
        );
487
        $fields->addFieldsToTab(
488
            'Root',
489
            array(
490
                Tab::create(
491
                    "Items",
492
                    _t('Order.ITEMS_TAB', 'Items')
493
                ),
494
                Tab::create(
495
                    "Extras",
496
                    _t('Order.MODIFIERS_TAB', 'Adjustments')
497
                ),
498
                Tab::create(
499
                    'Emails',
500
                    _t('Order.EMAILS_TAB', 'Emails')
501
                ),
502
                Tab::create(
503
                    'Payments',
504
                    _t('Order.PAYMENTS_TAB', 'Payment')
505
                ),
506
                Tab::create(
507
                    'Account',
508
                    _t('Order.ACCOUNT_TAB', 'Account')
509
                ),
510
                Tab::create(
511
                    'Currency',
512
                    _t('Order.CURRENCY_TAB', 'Currency')
513
                ),
514
                Tab::create(
515
                    'Addresses',
516
                    _t('Order.ADDRESSES_TAB', 'Addresses')
517
                ),
518
                Tab::create(
519
                    'Log',
520
                    _t('Order.LOG_TAB', 'Notes')
521
                ),
522
                Tab::create(
523
                    'Cancellations',
524
                    _t('Order.CANCELLATION_TAB', 'Cancel')
525
                ),
526
            )
527
        );
528
        //as we are no longer using the parent:;getCMSFields
529
        // we had to add the updateCMSFields hook.
530
        $this->extend('updateCMSFields', $fields);
531
        $currentMember = Member::currentUser();
532
        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...
533
            $firstStep = OrderStep::get()->First();
534
            $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...
535
            $this->write();
536
        }
537
        $submitted = $this->IsSubmitted() ? true : false;
538
        if ($submitted) {
539
            //TODO
540
            //Having trouble here, as when you submit the form (for example, a payment confirmation)
541
            //as the step moves forward, meaning the fields generated are incorrect, causing an error
542
            //"I can't handle sub-URLs of a Form object." generated by the RequestHandler.
543
            //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
544
            //Or something similar.
545
            //why not check if the URL == $this->CMSEditLink()
546
            //and only tryToFinaliseOrder if this is true....
547
            if ($_SERVER['REQUEST_URI'] == $this->CMSEditLink() || $_SERVER['REQUEST_URI'] == $this->CMSEditLink('edit')) {
548
                $this->tryToFinaliseOrder();
549
            }
550
        } else {
551
            $this->init(true);
552
            $this->calculateOrderAttributes(true);
553
            Session::set('EcommerceOrderGETCMSHack', $this->ID);
554
        }
555
        if ($submitted) {
556
            $this->fieldsAndTabsToBeRemoved[] = 'CustomerOrderNote';
557
        } else {
558
            $this->fieldsAndTabsToBeRemoved[] = 'Emails';
559
        }
560
        foreach ($this->fieldsAndTabsToBeRemoved as $field) {
561
            $fields->removeByName($field);
562
        }
563
        $orderSummaryConfig = GridFieldConfig_Base::create();
564
        $orderSummaryConfig->removeComponentsByType('GridFieldToolbarHeader');
565
        // $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...
566
        $orderSummaryConfig->removeComponentsByType('GridFieldFilterHeader');
567
        $orderSummaryConfig->removeComponentsByType('GridFieldPageCount');
568
        $orderSummaryConfig->removeComponentsByType('GridFieldPaginator');
569
        $nextFieldArray = array(
570
            LiteralField::create('CssFix', '<style>#Root_Next h2 {padding: 0!important; margin: 0!important; margin-top: 2em!important;}</style>'),
571
            HeaderField::create('OrderSummaryHeader', _t('Order.THIS_ORDER_HEADER', 'Order Summary')),
572
            GridField::create(
573
                'OrderSummary',
574
                _t('Order.CURRENT_STATUS', 'Summary'),
575
                ArrayList::create(array($this)),
576
                $orderSummaryConfig
577
            ),
578
        );
579
        $keyNotes = OrderStatusLog::get()->filter(
580
            array(
581
                'OrderID' => $this->ID,
582
                'ClassName' => 'OrderStatusLog'
583
            )
584
        );
585
        if ($keyNotes->count()) {
586
            $notesSummaryConfig = GridFieldConfig_RecordViewer::create();
587
            $notesSummaryConfig->removeComponentsByType('GridFieldToolbarHeader');
588
            $notesSummaryConfig->removeComponentsByType('GridFieldFilterHeader');
589
            // $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...
590
            $notesSummaryConfig->removeComponentsByType('GridFieldPageCount');
591
            $notesSummaryConfig->removeComponentsByType('GridFieldPaginator');
592
            $nextFieldArray = array_merge(
593
                $nextFieldArray,
594
                array(
595
                    HeaderField::create('KeyNotesHeader', _t('Order.KEY_NOTES_HEADER', 'Key Notes')),
596
                    GridField::create(
597
                        'OrderStatusLogSummary',
598
                        _t('Order.CURRENT_KEY_NOTES', 'Key Notes'),
599
                        $keyNotes,
600
                        $notesSummaryConfig
601
                    )
602
                )
603
            );
604
        }
605
        $nextFieldArray = array_merge(
606
            $nextFieldArray,
607
            array(
608
                HeaderField::create('MyOrderStepHeader', _t('Order.CURRENT_STATUS', '1. Current Status')),
609
                $this->OrderStepField()
610
            )
611
        );
612
613
         //is the member is a shop admin they can always view it
614
        if (EcommerceRole::current_member_can_process_orders(Member::currentUser())) {
615
            $nextFieldArray = array_merge(
616
                $nextFieldArray,
617
                array(
618
                    HeaderField::create('OrderStepNextStepHeader', _t('Order.ACTION_NEXT_STEP', '2. Action Next Step')),
619
                    HeaderField::create('ActionNextStepManually', _t('Order.MANUAL_STATUS_CHANGE', '3. Move Order Along')),
620
                    LiteralField::create('OrderStepNextStepHeaderExtra', '<p>'._t('Order.NEEDTOREFRESH', 'If you have made any changes to the order then you will have to refresh or save this record to move it along.').'</p>'),
621
                    EcommerceCMSButtonField::create(
622
                        'StatusIDExplanation',
623
                        $this->CMSEditLink(),
624
                        _t('Order.REFRESH', 'refresh now')
625
                    )
626
                )
627
            );
628
        }
629
        $nextFieldArray = array_merge(
630
            $nextFieldArray,
631
            array(
632
                EcommerceCMSButtonField::create(
633
                    'AddNoteButton',
634
                    $this->CMSEditLink('ItemEditForm/field/OrderStatusLog/item/new'),
635
                    _t('Order.ADD_NOTE', 'Add Note')
636
                )
637
            )
638
        );
639
        $fields->addFieldsToTab(
640
            'Root.Next',
641
            $nextFieldArray
642
        );
643
644
        $this->MyStep()->addOrderStepFields($fields, $this);
645
646
        if ($submitted) {
647
            $permaLinkLabel = _t('Order.PERMANENT_LINK', 'Customer Link');
648
            $html = '<p>'.$permaLinkLabel.': <a href="'.$this->getRetrieveLink().'">'.$this->getRetrieveLink().'</a></p>';
649
            $shareLinkLabel = _t('Order.SHARE_LINK', 'Share Link');
650
            $html .= '<p>'.$shareLinkLabel.': <a href="'.$this->getShareLink().'">'.$this->getShareLink().'</a></p>';
651
            $js = "window.open(this.href, 'payment', 'toolbar=0,scrollbars=1,location=1,statusbar=1,menubar=0,resizable=1,width=800,height=600'); return false;";
652
            $link = $this->getPrintLink();
653
            $label = _t('Order.PRINT_INVOICE', 'invoice');
654
            $linkHTML = '<a href="'.$link.'" onclick="'.$js.'">'.$label.'</a>';
655
            $linkHTML .= ' | ';
656
            $link = $this->getPackingSlipLink();
657
            $label = _t('Order.PRINT_PACKING_SLIP', 'packing slip');
658
            $labelPrint = _t('Order.PRINT', 'Print');
659
            $linkHTML .= '<a href="'.$link.'" onclick="'.$js.'">'.$label.'</a>';
660
            $html .= '<h3>';
661
            $html .= $labelPrint.': '.$linkHTML;
662
            $html .= '</h3>';
663
664
            $fields->addFieldToTab(
665
                'Root.Main',
666
                LiteralField::create('getPrintLinkANDgetPackingSlipLink', $html)
667
            );
668
669
            //add order here as well.
670
            $fields->addFieldToTab(
671
                'Root.Main',
672
                new LiteralField(
673
                    'MainDetails',
674
                    '<iframe src="'.$this->getPrintLink().'" width="100%" height="2500" style="border: 5px solid #2e7ead; border-radius: 2px;"></iframe>')
675
            );
676
            $fields->addFieldsToTab(
677
                'Root.Items',
678
                array(
679
                    GridField::create(
680
                        'Items_Sold',
681
                        'Items Sold',
682
                        $this->Items(),
683
                        new GridFieldConfig_RecordViewer
684
                    )
685
                )
686
            );
687
            $fields->addFieldsToTab(
688
                'Root.Extras',
689
                array(
690
                    GridField::create(
691
                        'Modifications',
692
                        'Price (and other) adjustments',
693
                        $this->Modifiers(),
694
                        new GridFieldConfig_RecordViewer
695
                    )
696
                )
697
            );
698
            $fields->addFieldsToTab(
699
                'Root.Emails',
700
                array(
701
                    $this->getEmailsTableField()
702
                )
703
            );
704
            $fields->addFieldsToTab(
705
                'Root.Payments',
706
                array(
707
                    $this->getPaymentsField(),
708
                    new ReadOnlyField('TotalPaidNice', _t('Order.TOTALPAID', 'Total Paid'), $this->TotalPaidAsCurrencyObject()->Nice()),
709
                    new ReadOnlyField('TotalOutstandingNice', _t('Order.TOTALOUTSTANDING', 'Total Outstanding'), $this->getTotalOutstandingAsMoney()->Nice())
710
                )
711
            );
712
            if ($this->canPay()) {
713
                $link = EcommercePaymentController::make_payment_link($this->ID);
714
                $js = "window.open(this.href, 'payment', 'toolbar=0,scrollbars=1,location=1,statusbar=1,menubar=0,resizable=1,width=800,height=600'); return false;";
715
                $header = _t('Order.MAKEPAYMENT', 'make payment');
716
                $label = _t('Order.MAKEADDITIONALPAYMENTNOW', 'make additional payment now');
717
                $linkHTML = '<a href="'.$link.'" onclick="'.$js.'">'.$label.'</a>';
718
                $fields->addFieldToTab('Root.Payments', new HeaderField('MakeAdditionalPaymentHeader', $header, 3));
719
                $fields->addFieldToTab('Root.Payments', new LiteralField('MakeAdditionalPayment', $linkHTML));
720
            }
721
            //member
722
            $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...
723
            if ($member && $member->exists()) {
724
                $fields->addFieldToTab('Root.Account', new LiteralField('MemberDetails', $member->getEcommerceFieldsForCMS()));
725
            } else {
726
                $fields->addFieldToTab('Root.Account', new LiteralField('MemberDetails',
727
                    '<p>'._t('Order.NO_ACCOUNT', 'There is no --- account --- associated with this order').'</p>'
728
                ));
729
            }
730
            $cancelledField = $fields->dataFieldByName('CancelledByID');
731
            $fields->removeByName('CancelledByID');
732
            $shopAdminAndCurrentCustomerArray = EcommerceRole::list_of_admins(true);
733
            if ($member && $member->exists()) {
734
                $shopAdminAndCurrentCustomerArray[$member->ID] = $member->getName();
735
            }
736
            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...
737
                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...
738
                    $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...
739
                }
740
            }
741
            if ($this->canCancel()) {
742
                $fields->addFieldsToTab(
743
                    'Root.Cancellations',
744
                    array(
745
                        DropdownField::create(
746
                            'CancelledByID',
747
                            $cancelledField->Title(),
748
                            $shopAdminAndCurrentCustomerArray
749
                        )
750
                    )
751
                );
752
            } else {
753
                $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...
754
                $fields->addFieldsToTab(
755
                    'Root.Cancellations',
756
                    ReadonlyField::create(
757
                        'CancelledByDisplay',
758
                        $cancelledField->Title(),
759
                        $cancelledBy
760
761
                    )
762
                );
763
            }
764
            $fields->addFieldToTab('Root.Log', $this->getOrderStatusLogsTableField_Archived());
765
            $submissionLog = $this->SubmissionLog();
766
            if ($submissionLog) {
767
                $fields->addFieldToTab('Root.Log',
768
                    new ReadonlyField(
769
                        'SequentialOrderNumber',
770
                        _t('Order.SEQUENTIALORDERNUMBER', 'Consecutive order number for submitted orders (e.g. 1,2,3,4,5...)'),
771
                        $submissionLog->SequentialOrderNumber
772
                    )
773
                );
774
            }
775
        } else {
776
            $linkText = _t(
777
                'Order.LOAD_THIS_ORDER',
778
                'load this order'
779
            );
780
            $message = _t(
781
                'Order.NOSUBMITTEDYET',
782
                '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 .',
783
                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...
784
            );
785
            $fields->addFieldToTab('Root.Next', new LiteralField('MainDetails', '<p>'.$message.'</p>'));
786
            $fields->addFieldToTab('Root.Items', $this->getOrderItemsField());
787
            $fields->addFieldToTab('Root.Extras', $this->getModifierTableField());
788
789
            //MEMBER STUFF
790
            $specialOptionsArray = array();
791
            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...
792
                $specialOptionsArray[0] = _t('Order.SELECTCUSTOMER', '--- Remover Customer ---');
793
                $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...
794
            } elseif ($currentMember) {
795
                $specialOptionsArray[0] = _t('Order.SELECTCUSTOMER', '--- Select Customers ---');
796
                $currentMemberID = $currentMember->ID;
797
                $specialOptionsArray[$currentMemberID] = _t('Order.ASSIGNTHISORDERTOME', '- Assign this order to me: ').$currentMember->getTitle();
798
            }
799
            //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...
800
            $memberArray = $specialOptionsArray + EcommerceRole::list_of_customers(true);
801
            $fields->addFieldToTab('Root.Next', new DropdownField('MemberID', _t('Order.SELECTCUSTOMER', 'Select Customer'), $memberArray), 'CustomerOrderNote');
802
            $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...
803
        }
804
        $fields->addFieldToTab('Root.Addresses', new HeaderField('BillingAddressHeader', _t('Order.BILLINGADDRESS', 'Billing Address')));
805
806
        $fields->addFieldToTab('Root.Addresses', $this->getBillingAddressField());
807
808
        if (EcommerceConfig::get('OrderAddress', 'use_separate_shipping_address')) {
809
            $fields->addFieldToTab('Root.Addresses', new HeaderField('ShippingAddressHeader', _t('Order.SHIPPINGADDRESS', 'Shipping Address')));
810
            $fields->addFieldToTab('Root.Addresses', new CheckboxField('UseShippingAddress', _t('Order.USESEPERATEADDRESS', 'Use separate shipping address?')));
811
            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...
812
                $fields->addFieldToTab('Root.Addresses', $this->getShippingAddressField());
813
            }
814
        }
815
        $currencies = EcommerceCurrency::get_list();
816
        if ($currencies && $currencies->count()) {
817
            $currencies = $currencies->map()->toArray();
818
            $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...
819
            $fields->addFieldToTab('Root.Currency', $currencyField = new DropdownField('CurrencyUsedID', _t('Order.CurrencyUsed', 'Currency Used'), $currencies));
820
            if ($this->IsSubmitted()) {
821
                $fields->replaceField('CurrencyUsedID', $fields->dataFieldByName('CurrencyUsedID')->performReadonlyTransformation());
822
            }
823
        } else {
824
            $fields->addFieldToTab('Root.Currency', new LiteralField('CurrencyInfo', '<p>You can not change currencies, because no currencies have been created.</p>'));
825
            $fields->replaceField('CurrencyUsedID', $fields->dataFieldByName('CurrencyUsedID')->performReadonlyTransformation());
826
        }
827
        $fields->addFieldToTab('Root.Log', new ReadonlyField('Created', _t('Root.CREATED', 'Created')));
828
        $fields->addFieldToTab('Root.Log', new ReadonlyField('LastEdited', _t('Root.LASTEDITED', 'Last saved')));
829
        $this->extend('updateCMSFields', $fields);
830
831
        return $fields;
832
    }
833
834
    /**
835
     * Field to add and edit Order Items.
836
     *
837
     * @return GridField
838
     */
839
    protected function getOrderItemsField()
840
    {
841
        $gridFieldConfig = GridFieldConfigForOrderItems::create();
842
        $source = $this->OrderItems();
843
844
        return new GridField('OrderItems', _t('OrderItems.PLURALNAME', 'Order Items'), $source, $gridFieldConfig);
845
    }
846
847
    /**
848
     * Field to add and edit Modifiers.
849
     *
850
     * @return GridField
851
     */
852
    public function getModifierTableField()
853
    {
854
        $gridFieldConfig = GridFieldConfigForOrderItems::create();
855
        $source = $this->Modifiers();
856
857
        return new GridField('OrderModifiers', _t('OrderItems.PLURALNAME', 'Order Items'), $source, $gridFieldConfig);
858
    }
859
860
    /**
861
     *@return GridField
862
     **/
863
    protected function getBillingAddressField()
864
    {
865
        $this->CreateOrReturnExistingAddress('BillingAddress');
866
        $gridFieldConfig = GridFieldConfig::create()->addComponents(
867
            new GridFieldToolbarHeader(),
868
            new GridFieldSortableHeader(),
869
            new GridFieldDataColumns(),
870
            new GridFieldPaginator(10),
871
            new GridFieldEditButton(),
872
            new GridFieldDetailForm()
873
        );
874
        //$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...
875
        $source = BillingAddress::get()->filter(array('OrderID' => $this->ID));
876
877
        return new GridField('BillingAddress', _t('BillingAddress.SINGULARNAME', 'Billing Address'), $source, $gridFieldConfig);
878
    }
879
880
    /**
881
     *@return GridField
882
     **/
883
    protected function getShippingAddressField()
884
    {
885
        $this->CreateOrReturnExistingAddress('ShippingAddress');
886
        $gridFieldConfig = GridFieldConfig::create()->addComponents(
887
            new GridFieldToolbarHeader(),
888
            new GridFieldSortableHeader(),
889
            new GridFieldDataColumns(),
890
            new GridFieldPaginator(10),
891
            new GridFieldEditButton(),
892
            new GridFieldDetailForm()
893
        );
894
        //$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...
895
        $source = ShippingAddress::get()->filter(array('OrderID' => $this->ID));
896
897
        return new GridField('ShippingAddress', _t('BillingAddress.SINGULARNAME', 'Shipping Address'), $source, $gridFieldConfig);
898
    }
899
900
    /**
901
     * Needs to be public because the OrderStep::getCMSFIelds accesses it.
902
     *
903
     * @param string    $sourceClass
904
     * @param string    $title
905
     *
906
     * @return GridField
907
     **/
908
    public function getOrderStatusLogsTableField(
909
        $sourceClass = 'OrderStatusLog',
910
        $title = ''
911
    ) {
912
        $gridFieldConfig = GridFieldConfig_RecordViewer::create()->addComponents(
913
            new GridFieldAddNewButton('toolbar-header-right'),
914
            new GridFieldDetailForm()
915
        );
916
        $title ? $title : $title = _t('OrderStatusLog.PLURALNAME', 'Order Status Logs');
917
        $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...
918
        $gf = new GridField($sourceClass, $title, $source, $gridFieldConfig);
919
        $gf->setModelClass($sourceClass);
920
921
        return $gf;
922
    }
923
924
    /**
925
     * Needs to be public because the OrderStep::getCMSFIelds accesses it.
926
     *
927
     * @param string    $sourceClass
928
     * @param string    $title
929
     *
930
     * @return GridField
931
     **/
932
    public function getOrderStatusLogsTableFieldEditable(
933
        $sourceClass = 'OrderStatusLog',
934
        $title = ''
935
    ) {
936
        $gf = $this->getOrderStatusLogsTableField($sourceClass, $title);
937
        $gf->getConfig()->addComponents(
938
            new GridFieldEditButton()
939
        );
940
        return $gf;
941
    }
942
943
    /**
944
     * @param string    $sourceClass
945
     * @param string    $title
946
     * @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...
947
     * @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...
948
     *
949
     * @return GridField
950
     **/
951
    protected function getOrderStatusLogsTableField_Archived(
952
        $sourceClass = 'OrderStatusLog',
953
        $title = '',
954
        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...
955
        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...
956
    ) {
957
        $title ? $title : $title = _t('OrderLog.PLURALNAME', 'Order Log');
958
        $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...
959
        if ($sourceClass != 'OrderStatusLog' && class_exists($sourceClass)) {
960
            $source = $source->filter(array('ClassName' => ClassInfo::subclassesFor($sourceClass)));
961
        }
962
        $gridField = GridField::create($sourceClass, $title, $source, $config = GridFieldConfig_RelationEditor::create());
963
        $config->removeComponentsByType('GridFieldAddExistingAutocompleter');
964
        $config->removeComponentsByType('GridFieldDeleteAction');
965
966
        return $gridField;
967
    }
968
969
    /**
970
     * @return GridField
971
     **/
972
    public function getEmailsTableField()
973
    {
974
        $gridFieldConfig = GridFieldConfig_RecordViewer::create()->addComponents(
975
            new GridFieldDetailForm()
976
        );
977
978
        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...
979
    }
980
981
    /**
982
     * @return GridField
983
     */
984
    protected function getPaymentsField()
985
    {
986
        $gridFieldConfig = GridFieldConfig_RecordViewer::create()->addComponents(
987
            new GridFieldDetailForm(),
988
            new GridFieldEditButton()
989
        );
990
991
        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...
992
    }
993
994
    /**
995
     * @return OrderStepField
996
     */
997
    public function OrderStepField()
998
    {
999
        return OrderStepField::create($name = 'MyOrderStep', $this, Member::currentUser());
1000
    }
1001
1002
/*******************************************************
1003
   * 2. MAIN TRANSITION FUNCTIONS
1004
*******************************************************/
1005
1006
    /**
1007
     * init runs on start of a new Order (@see onAfterWrite)
1008
     * it adds all the modifiers to the orders and the starting OrderStep.
1009
     *
1010
     * @param bool $recalculate
1011
     *
1012
     * @return DataObject (Order)
1013
     **/
1014
    public function init($recalculate = false)
1015
    {
1016
        if ($this->IsSubmitted()) {
1017
            user_error('Can not init an order that has been submitted', E_USER_NOTICE);
1018
        } else {
1019
            //to do: check if shop is open....
1020
            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...
1021
                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...
1022
                    $createdOrderStatus = OrderStep::get()->First();
1023
                    if (!$createdOrderStatus) {
1024
                        user_error('No ordersteps have been created', E_USER_WARNING);
1025
                    }
1026
                    $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...
1027
                }
1028
                $createdModifiersClassNames = array();
1029
                $modifiersAsArrayList = new ArrayList();
1030
                $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...
1031
                if ($modifiers->count()) {
1032
                    foreach ($modifiers as $modifier) {
1033
                        $modifiersAsArrayList->push($modifier);
1034
                    }
1035
                }
1036
                if ($modifiersAsArrayList->count()) {
1037
                    foreach ($modifiersAsArrayList as $modifier) {
1038
                        $createdModifiersClassNames[$modifier->ID] = $modifier->ClassName;
1039
                    }
1040
                } 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...
1041
                }
1042
                $modifiersToAdd = EcommerceConfig::get('Order', 'modifiers');
1043
                if (is_array($modifiersToAdd) && count($modifiersToAdd) > 0) {
1044
                    foreach ($modifiersToAdd as $numericKey => $className) {
1045
                        if (!in_array($className, $createdModifiersClassNames)) {
1046
                            if (class_exists($className)) {
1047
                                $modifier = new $className();
1048
                                //only add the ones that should be added automatically
1049
                                if (!$modifier->DoNotAddAutomatically()) {
1050
                                    if (is_a($modifier, 'OrderModifier')) {
1051
                                        $modifier->OrderID = $this->ID;
1052
                                        $modifier->Sort = $numericKey;
1053
                                        //init method includes a WRITE
1054
                                        $modifier->init();
1055
                                        //IMPORTANT - add as has_many relationship  (Attributes can be a modifier OR an OrderItem)
1056
                                        $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...
1057
                                        $modifiersAsArrayList->push($modifier);
1058
                                    }
1059
                                }
1060
                            } else {
1061
                                user_error('reference to a non-existing class: '.$className.' in modifiers', E_USER_NOTICE);
1062
                            }
1063
                        }
1064
                    }
1065
                }
1066
                $this->extend('onInit', $this);
1067
                //careful - this will call "onAfterWrite" again
1068
                $this->write();
1069
            }
1070
        }
1071
1072
        return $this;
1073
    }
1074
1075
    /**
1076
     * @var array
1077
     */
1078
    private static $_try_to_finalise_order_is_running = array();
1079
1080
    /**
1081
     * Goes through the order steps and tries to "apply" the next status to the order.
1082
     *
1083
     * @param bool $runAgain
1084
     * @param bool $fromOrderQueue - is it being called from the OrderProcessQueue (or similar)
1085
     **/
1086
    public function tryToFinaliseOrder($runAgain = false, $fromOrderQueue = false)
1087
    {
1088
        if (empty(self::$_try_to_finalise_order_is_running[$this->ID]) || $runAgain) {
1089
            self::$_try_to_finalise_order_is_running[$this->ID] = true;
1090
            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...
1091
                $this->Archive(true);
1092
1093
                return;
1094
            }
1095
            //does a queue object already exist
1096
            $queueObjectSingleton = Injector::inst()->get('OrderProcessQueue');
1097
            if ($myQueueObject = $queueObjectSingleton->getQueueObject($this)) {
1098
                if ($myQueueObject->InProcess) {
1099
                    if (! $fromOrderQueue) {
1100
                        return;
1101
                    }
1102
                }
1103
            }
1104
            //a little hack to make sure we do not rely on a stored value
1105
            //of "isSubmitted"
1106
            $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...
1107
            //status of order is being progressed
1108
            $nextStatusID = $this->doNextStatus();
1109
            if ($nextStatusID) {
1110
                $nextStatusObject = OrderStep::get()->byID($nextStatusID);
1111
                if ($nextStatusObject) {
1112
                    $delay = $nextStatusObject->CalculatedDeferTimeInSeconds($this);
1113
                    if ($delay > 0) {
1114
                        if ($nextStatusObject->DeferFromSubmitTime) {
1115
                            $delay = $delay - $this->SecondsSinceBeingSubmitted();
1116
                            if ($delay < 0) {
1117
                                $delay = 0;
1118
                            }
1119
                        }
1120
                        $queueObjectSingleton->AddOrderToQueue(
1121
                            $this,
1122
                            $delay
1123
                        );
1124
                    } else {
1125
                        //status has been completed, so it can be released
1126
                        self::$_try_to_finalise_order_is_running[$this->ID] = false;
1127
                        $this->tryToFinaliseOrder($runAgain, $fromOrderQueue);
1128
                    }
1129
                }
1130
            }
1131
        }
1132
    }
1133
1134
    /**
1135
     * Goes through the order steps and tries to "apply" the next step
1136
     * Step is updated after the other one is completed...
1137
     *
1138
     * @return int (StatusID or false if the next status can not be "applied")
1139
     **/
1140
    public function doNextStatus()
1141
    {
1142
        if ($this->MyStep()->initStep($this)) {
1143
            if ($this->MyStep()->doStep($this)) {
1144
                if ($nextOrderStepObject = $this->MyStep()->nextStep($this)) {
1145
                    $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...
1146
                    $this->write();
1147
1148
                    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...
1149
                }
1150
            }
1151
        }
1152
1153
        return 0;
1154
    }
1155
1156
    /**
1157
     * cancel an order.
1158
     *
1159
     * @param Member $member - the user cancelling the order
1160
     * @param string $reason - the reason the order is cancelled
1161
     *
1162
     * @return OrderStatusLog_Cancel
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...
1163
     */
1164
    public function Cancel(Member $member, $reason = '')
1165
    {
1166
        $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...
1167
        //archive and write
1168
        $this->Archive($avoidWrites = true);
1169
        //create log ...
1170
        $log = OrderStatusLog_Cancel::create();
1171
        $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...
1172
        $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...
1173
        $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...
1174
        if ($member->IsShopAdmin()) {
1175
            $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...
1176
        }
1177
1178
        return $log->write();
1179
    }
1180
1181
    /**
1182
     * returns true if successful.
1183
     *
1184
     * @param bool $avoidWrites
1185
     *
1186
     * @return bool
1187
     */
1188
    public function Archive($avoidWrites = true)
1189
    {
1190
        $lastOrderStep = OrderStep::get()->Last();
1191
        if ($lastOrderStep) {
1192
            if ($avoidWrites) {
1193
                DB::query('
1194
                    UPDATE "Order"
1195
                    SET "Order"."StatusID" = '.$lastOrderStep->ID.'
1196
                    WHERE "Order"."ID" = '.$this->ID.'
1197
                    LIMIT 1
1198
                ');
1199
1200
                return true;
1201
            } else {
1202
                $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...
1203
                $this->write();
1204
1205
                return true;
1206
            }
1207
        }
1208
1209
        return false;
1210
    }
1211
1212
/*******************************************************
1213
   * 3. STATUS RELATED FUNCTIONS / SHORTCUTS
1214
*******************************************************/
1215
1216
    /**
1217
     * Avoids caching of $this->Status().
1218
     *
1219
     * @return DataObject (current OrderStep)
1220
     */
1221
    public function MyStep()
1222
    {
1223
        $step = null;
1224
        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...
1225
            $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...
1226
        }
1227
        if (!$step) {
1228
            $step = OrderStep::get()->First(); //TODO: this could produce strange results
1229
        }
1230
        if (!$step) {
1231
            $step = OrderStep_Created::create();
1232
        }
1233
        if (!$step) {
1234
            user_error('You need an order step in your Database.');
1235
        }
1236
1237
        return $step;
1238
    }
1239
1240
    /**
1241
     * Return the OrderStatusLog that is relevant to the Order status.
1242
     *
1243
     * @return OrderStatusLog
1244
     */
1245
    public function RelevantLogEntry()
1246
    {
1247
        return $this->MyStep()->RelevantLogEntry($this);
1248
    }
1249
1250
    /**
1251
     * @return OrderStep (current OrderStep that can be seen by customer)
0 ignored issues
show
Documentation introduced by
Should the return type not be OrderStep|null?

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

Loading history...
1252
     */
1253
    public function CurrentStepVisibleToCustomer()
1254
    {
1255
        $obj = $this->MyStep();
1256
        if ($obj->HideStepFromCustomer) {
1257
            $obj = OrderStep::get()->where('"OrderStep"."Sort" < '.$obj->Sort.' AND "HideStepFromCustomer" = 0')->Last();
1258
            if (!$obj) {
1259
                $obj = OrderStep::get()->First();
1260
            }
1261
        }
1262
1263
        return $obj;
1264
    }
1265
1266
    /**
1267
     * works out if the order is still at the first OrderStep.
1268
     *
1269
     * @return bool
1270
     */
1271
    public function IsFirstStep()
1272
    {
1273
        $firstStep = OrderStep::get()->First();
1274
        $currentStep = $this->MyStep();
1275
        if ($firstStep && $currentStep) {
1276
            if ($firstStep->ID == $currentStep->ID) {
1277
                return true;
1278
            }
1279
        }
1280
1281
        return false;
1282
    }
1283
1284
    /**
1285
     * Is the order still being "edited" by the customer?
1286
     *
1287
     * @return bool
1288
     */
1289
    public function IsInCart()
1290
    {
1291
        return (bool) $this->IsSubmitted() ? false : true;
1292
    }
1293
1294
    /**
1295
     * The order has "passed" the IsInCart phase.
1296
     *
1297
     * @return bool
1298
     */
1299
    public function IsPastCart()
1300
    {
1301
        return (bool) $this->IsInCart() ? false : true;
1302
    }
1303
1304
    /**
1305
     * Are there still steps the order needs to go through?
1306
     *
1307
     * @return bool
1308
     */
1309
    public function IsUncomplete()
1310
    {
1311
        return (bool) $this->MyStep()->ShowAsUncompletedOrder;
1312
    }
1313
1314
    /**
1315
     * Is the order in the :"processing" phaase.?
1316
     *
1317
     * @return bool
1318
     */
1319
    public function IsProcessing()
1320
    {
1321
        return (bool) $this->MyStep()->ShowAsInProcessOrder;
1322
    }
1323
1324
    /**
1325
     * Is the order completed?
1326
     *
1327
     * @return bool
1328
     */
1329
    public function IsCompleted()
1330
    {
1331
        return (bool) $this->MyStep()->ShowAsCompletedOrder;
1332
    }
1333
1334
    /**
1335
     * Has the order been paid?
1336
     * TODO: why do we check if there is a total at all?
1337
     *
1338
     * @return bool
1339
     */
1340
    public function IsPaid()
1341
    {
1342
        if ($this->IsSubmitted()) {
1343
            return (bool) (($this->Total() >= 0) && ($this->TotalOutstanding() <= 0));
1344
        }
1345
1346
        return false;
1347
    }
1348
1349
    /**
1350
     * Has the order been paid?
1351
     * TODO: why do we check if there is a total at all?
1352
     *
1353
     * @return bool
1354
     */
1355
    public function PaymentIsPending()
1356
    {
1357
        if ($this->IsSubmitted()) {
1358
            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...
1359
                //do nothing;
1360
            } 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...
1361
                foreach ($payments as $payment) {
1362
                    if ('Pending' == $payment->Status) {
1363
                        return true;
1364
                    }
1365
                }
1366
            }
1367
        }
1368
1369
        return false;
1370
    }
1371
1372
    /**
1373
     * shows payments that are meaningfull
1374
     * if the order has been paid then only show successful payments.
1375
     *
1376
     * @return DataList
1377
     */
1378
    public function RelevantPayments()
1379
    {
1380
        if ($this->IsPaid()) {
1381
            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...
1382
            //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...
1383
            //	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...
1384
        } else {
1385
            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...
1386
        }
1387
    }
1388
1389
    /**
1390
     * Has the order been cancelled?
1391
     *
1392
     * @return bool
1393
     */
1394
    public function IsCancelled()
1395
    {
1396
        return $this->getIsCancelled();
1397
    }
1398
    public function getIsCancelled()
1399
    {
1400
        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...
1401
    }
1402
1403
    /**
1404
     * Has the order been cancelled by the customer?
1405
     *
1406
     * @return bool
1407
     */
1408
    public function IsCustomerCancelled()
1409
    {
1410
        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...
1411
            return true;
1412
        }
1413
1414
        return false;
1415
    }
1416
1417
    /**
1418
     * Has the order been cancelled by the  administrator?
1419
     *
1420
     * @return bool
1421
     */
1422
    public function IsAdminCancelled()
1423
    {
1424
        if ($this->IsCancelled()) {
1425
            if (!$this->IsCustomerCancelled()) {
1426
                $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...
1427
                if ($admin) {
1428
                    if ($admin->IsShopAdmin()) {
1429
                        return true;
1430
                    }
1431
                }
1432
            }
1433
        }
1434
1435
        return false;
1436
    }
1437
1438
    /**
1439
     * Is the Shop Closed for business?
1440
     *
1441
     * @return bool
1442
     */
1443
    public function ShopClosed()
1444
    {
1445
        return EcomConfig()->ShopClosed;
1446
    }
1447
1448
/*******************************************************
1449
   * 4. LINKING ORDER WITH MEMBER AND ADDRESS
1450
*******************************************************/
1451
1452
    /**
1453
     * Returns a member linked to the order.
1454
     * If a member is already linked, it will return the existing member.
1455
     * Otherwise it will return a new Member.
1456
     *
1457
     * Any new member is NOT written, because we dont want to create a new member unless we have to!
1458
     * We will not add a member to the order unless a new one is created in the checkout
1459
     * OR the member is logged in / logs in.
1460
     *
1461
     * Also note that if a new member is created, it is not automatically written
1462
     *
1463
     * @param bool $forceCreation - if set to true then the member will always be saved in the database.
1464
     *
1465
     * @return Member
1466
     **/
1467
    public function CreateOrReturnExistingMember($forceCreation = false)
1468
    {
1469
        if ($this->IsSubmitted()) {
1470
            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...
1471
        }
1472
        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...
1473
            $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...
1474
        } elseif ($member = Member::currentUser()) {
1475
            if (!$member->IsShopAdmin()) {
1476
                $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...
1477
                $this->write();
1478
            }
1479
        }
1480
        $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...
1481
        if (!$member) {
1482
            $member = new Member();
1483
        }
1484
        if ($member && $forceCreation) {
1485
            $member->write();
1486
        }
1487
1488
        return $member;
1489
    }
1490
1491
    /**
1492
     * Returns either the existing one or a new Order Address...
1493
     * All Orders will have a Shipping and Billing address attached to it.
1494
     * Method used to retrieve object e.g. for $order->BillingAddress(); "BillingAddress" is the method name you can use.
1495
     * If the method name is the same as the class name then dont worry about providing one.
1496
     *
1497
     * @param string $className             - ClassName of the Address (e.g. BillingAddress or ShippingAddress)
1498
     * @param string $alternativeMethodName - method to retrieve Address
1499
     **/
1500
    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...
1501
    {
1502
        if ($this->exists()) {
1503
            $methodName = $className;
1504
            if ($alternativeMethodName) {
1505
                $methodName = $alternativeMethodName;
1506
            }
1507
            if ($this->IsSubmitted()) {
1508
                return $this->$methodName();
1509
            }
1510
            $variableName = $className.'ID';
1511
            $address = null;
1512
            if ($this->$variableName) {
1513
                $address = $this->$methodName();
1514
            }
1515
            if (!$address) {
1516
                $address = new $className();
1517
                if ($member = $this->CreateOrReturnExistingMember()) {
1518
                    if ($member->exists()) {
1519
                        $address->FillWithLastAddressFromMember($member, $write = false);
1520
                    }
1521
                }
1522
            }
1523
            if ($address) {
1524
                if (!$address->exists()) {
1525
                    $address->write();
1526
                }
1527
                if ($address->OrderID != $this->ID) {
1528
                    $address->OrderID = $this->ID;
1529
                    $address->write();
1530
                }
1531
                if ($this->$variableName != $address->ID) {
1532
                    if (!$this->IsSubmitted()) {
1533
                        $this->$variableName = $address->ID;
1534
                        $this->write();
1535
                    }
1536
                }
1537
1538
                return $address;
1539
            }
1540
        }
1541
1542
        return;
1543
    }
1544
1545
    /**
1546
     * Sets the country in the billing and shipping address.
1547
     *
1548
     * @param string $countryCode            - code for the country e.g. NZ
1549
     * @param bool   $includeBillingAddress
1550
     * @param bool   $includeShippingAddress
1551
     **/
1552
    public function SetCountryFields($countryCode, $includeBillingAddress = true, $includeShippingAddress = true)
1553
    {
1554
        if ($this->IsSubmitted()) {
1555
            user_error('Can not change country in submitted order', E_USER_NOTICE);
1556
        } else {
1557
            if ($includeBillingAddress) {
1558
                if ($billingAddress = $this->CreateOrReturnExistingAddress('BillingAddress')) {
1559
                    $billingAddress->SetCountryFields($countryCode);
1560
                }
1561
            }
1562
            if (EcommerceConfig::get('OrderAddress', 'use_separate_shipping_address')) {
1563
                if ($includeShippingAddress) {
1564
                    if ($shippingAddress = $this->CreateOrReturnExistingAddress('ShippingAddress')) {
1565
                        $shippingAddress->SetCountryFields($countryCode);
1566
                    }
1567
                }
1568
            }
1569
        }
1570
    }
1571
1572
    /**
1573
     * Sets the region in the billing and shipping address.
1574
     *
1575
     * @param int $regionID - ID for the region to be set
1576
     **/
1577
    public function SetRegionFields($regionID)
1578
    {
1579
        if ($this->IsSubmitted()) {
1580
            user_error('Can not change country in submitted order', E_USER_NOTICE);
1581
        } else {
1582
            if ($billingAddress = $this->CreateOrReturnExistingAddress('BillingAddress')) {
1583
                $billingAddress->SetRegionFields($regionID);
1584
            }
1585
            if ($this->CanHaveShippingAddress()) {
1586
                if ($shippingAddress = $this->CreateOrReturnExistingAddress('ShippingAddress')) {
1587
                    $shippingAddress->SetRegionFields($regionID);
1588
                }
1589
            }
1590
        }
1591
    }
1592
1593
    /**
1594
     * Stores the preferred currency of the order.
1595
     * IMPORTANTLY we store the exchange rate for future reference...
1596
     *
1597
     * @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...
1598
     */
1599
    public function UpdateCurrency($newCurrency)
1600
    {
1601
        if ($this->IsSubmitted()) {
1602
            user_error('Can not set the currency after the order has been submitted', E_USER_NOTICE);
1603
        } else {
1604
            if (! is_a($newCurrency, Object::getCustomClass('EcommerceCurrency'))) {
1605
                $newCurrency = EcommerceCurrency::default_currency();
1606
            }
1607
            $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...
1608
            $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...
1609
            $this->write();
1610
        }
1611
    }
1612
1613
    /**
1614
     * alias for UpdateCurrency.
1615
     *
1616
     * @param EcommerceCurrency $currency
1617
     */
1618
    public function SetCurrency($currency)
1619
    {
1620
        $this->UpdateCurrency($currency);
1621
    }
1622
1623
/*******************************************************
1624
   * 5. CUSTOMER COMMUNICATION
1625
*******************************************************/
1626
1627
    /**
1628
     * Send the invoice of the order by email.
1629
     *
1630
     * @param string $subject            - subject for the email
1631
     * @param string $message            - the main message in the email
1632
     * @param bool   $resend             - send the email even if it has been sent before
1633
     * @param bool   $adminOnlyOrToEmail - do not send to customer, only send to shop admin
1634
     * @param string $emailClassName     - class used to send email
1635
     *
1636
     * @return bool TRUE on success, FALSE on failure (in theory)
1637
     */
1638
    public function sendEmail(
1639
        $subject = '',
1640
        $message = '',
1641
        $resend = false,
1642
        $adminOnlyOrToEmail = false,
1643
        $emailClassName = 'Order_InvoiceEmail'
1644
    ) {
1645
        return $this->prepareAndSendEmail(
1646
            $emailClassName,
1647
            $subject,
1648
            $message,
1649
            $resend,
1650
            $adminOnlyOrToEmail
1651
        );
1652
    }
1653
1654
    /**
1655
     * Sends a message to the shop admin ONLY and not to the customer
1656
     * This can be used by ordersteps and orderlogs to notify the admin of any potential problems.
1657
     *
1658
     * @param string $subject - subject for the email
1659
     * @param string $message - message to be added with the email
1660
     *
1661
     * @return bool TRUE for success, FALSE for failure (not tested)
1662
     */
1663
    public function sendError($subject = '', $message = '')
1664
    {
1665
        return $this->prepareAndSendEmail('Order_ErrorEmail', _t('Order.ERROR', 'ERROR').' '.$subject, $message, $resend = true, $adminOnly = true);
1666
    }
1667
1668
    /**
1669
     * Sends a message to the shop admin ONLY and not to the customer
1670
     * This can be used by ordersteps and orderlogs to notify the admin of any potential problems.
1671
     *
1672
     * @param string $subject        - subject for the email
1673
     * @param string $message        - message to be added with the email
1674
     * @param bool   $resend         - can it be sent twice?
1675
     * @param string $emailClassName - template to be used ...
1676
     *
1677
     * @return bool TRUE for success, FALSE for failure (not tested)
1678
     */
1679
    public function sendAdminNotification(
1680
        $subject = '',
1681
        $message = '',
1682
        $resend = false,
1683
        $emailClassName = 'Order_ErrorEmail'
1684
    ) {
1685
        return $this->prepareAndSendEmail($emailClassName, $subject, $message, $resend, $adminOnly = true);
1686
    }
1687
1688
    /**
1689
     * Send a mail of the order to the client (and another to the admin).
1690
     *
1691
     * @param string        $emailClassName     - the class name of the email you wish to send
1692
     * @param string        $subject            - email subject
1693
     * @param bool          $copyToAdmin        - true by default, whether it should send a copy to the admin
0 ignored issues
show
Bug introduced by
There is no parameter named $copyToAdmin. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

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

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

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

Loading history...
1694
     * @param bool          $resend             - sends the email even it has been sent before.
1695
     * @param bool | string $adminOnlyOrToEmail - sends the email to the ADMIN ONLY, if you provide an email, it will go to the email...
1696
     *
1697
     * @return bool TRUE for success, FALSE for failure (not tested)
1698
     */
1699
    protected function prepareAndSendEmail(
1700
        $emailClassName,
1701
        $subject,
1702
        $message,
1703
        $resend = false,
1704
        $adminOnlyOrToEmail = false
1705
    ) {
1706
        $arrayData = $this->createReplacementArrayForEmail($message, $subject);
1707
        $from = Order_Email::get_from_email();
1708
        //why are we using this email and NOT the member.EMAIL?
1709
        //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...
1710
        if ($adminOnlyOrToEmail) {
1711
            if (filter_var($adminOnlyOrToEmail, FILTER_VALIDATE_EMAIL)) {
1712
                $to = $adminOnlyOrToEmail;
1713
                // invalid e-mail address
1714
            } else {
1715
                $to = Order_Email::get_from_email();
1716
            }
1717
        } else {
1718
            $to = $this->getOrderEmail();
1719
        }
1720
        if ($from && $to) {
1721
            $email = new $emailClassName();
1722
            if (!(is_a($email, Object::getCustomClass('Email')))) {
1723
                user_error('No correct email class provided.', E_USER_ERROR);
1724
            }
1725
            $email->setFrom($from);
1726
            $email->setTo($to);
1727
            //we take the subject from the Array Data, just in case it has been adjusted.
1728
            $email->setSubject($arrayData->getField('Subject'));
1729
            //we also see if a CC and a BCC have been added
1730
            ;
1731
            if ($cc = $arrayData->getField('CC')) {
1732
                $email->setCc($cc);
1733
            }
1734
            if ($bcc = $arrayData->getField('BCC')) {
1735
                $email->setBcc($bcc);
1736
            }
1737
            $email->populateTemplate($arrayData);
1738
            // This might be called from within the CMS,
1739
            // so we need to restore the theme, just in case
1740
            // templates within the theme exist
1741
            Config::nest();
1742
            Config::inst()->update('SSViewer', 'theme_enabled', true);
1743
            $email->setOrder($this);
1744
            $email->setResend($resend);
1745
            $result = $email->send(null);
1746
            Config::unnest();
1747
            if (Director::isDev()) {
1748
                return true;
1749
            } else {
1750
                return $result;
1751
            }
1752
        }
1753
1754
        return false;
1755
    }
1756
1757
    /**
1758
     * returns the Data that can be used in the body of an order Email
1759
     * we add the subject here so that the subject, for example, can be added to the <title>
1760
     * of the email template.
1761
     * we add the subject here so that the subject, for example, can be added to the <title>
1762
     * of the email template.
1763
     *
1764
     * @param string $message - the additional message
1765
     * @param string $subject - subject for email -
1766
     *
1767
     * @return ArrayData
1768
     *                   - Subject - EmailSubject
1769
     *                   - Message - specific message for this order
1770
     *                   - OrderStepMessage - generic message for step
1771
     *                   - Order
1772
     *                   - EmailLogo
1773
     *                   - ShopPhysicalAddress
1774
     *                   - CurrentDateAndTime
1775
     *                   - BaseURL
1776
     *                   - CC
1777
     *                   - BCC
1778
     */
1779
    public function createReplacementArrayForEmail($message = '', $subject = '')
1780
    {
1781
        $step = $this->MyStep();
1782
        $config = $this->EcomConfig();
1783
        $replacementArray = array();
1784
        //set subject
1785
        if ($subject) {
1786
            $subject = $subject;
0 ignored issues
show
Bug introduced by
Why assign $subject to itself?

This checks looks for cases where a variable has been assigned to itself.

This assignement can be removed without consequences.

Loading history...
1787
        } else {
1788
            $subject = $step->EmailSubject;
1789
        }
1790
        $subject = str_replace('[OrderNumber]', $this->ID, $subject);
1791
        //set other variables
1792
        $replacementArray['Subject'] = $subject;
1793
        $replacementArray['To'] = '';
1794
        $replacementArray['CC'] = '';
1795
        $replacementArray['BCC'] = '';
1796
        $replacementArray['Message'] = $message;
1797
        $replacementArray['OrderStepMessage'] = $step->CustomerMessage;
1798
        $replacementArray['Order'] = $this;
1799
        $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...
1800
        $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...
1801
        $replacementArray['CurrentDateAndTime'] = DBField::create_field('SS_Datetime', 'Now');
1802
        $replacementArray['BaseURL'] = Director::baseURL();
1803
        $arrayData = new ArrayData($replacementArray);
1804
        $this->extend('updateReplacementArrayForEmail', $arrayData);
1805
        return $arrayData;
1806
    }
1807
1808
    /**
1809
     * returns the order formatted as an email.
1810
     *
1811
     * @param string $message        - the additional message
1812
     * @param string $emailClassName - template to use.
1813
     *
1814
     * @return array (Message, Order, EmailLogo, ShopPhysicalAddress)
0 ignored issues
show
Documentation introduced by
Should the return type not be string?

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

Loading history...
1815
     */
1816
    public function renderOrderInEmailFormat($message = '', $emailClassName)
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...
1817
    {
1818
        $arrayData = $this->createReplacementArrayForEmail($message);
1819
        Config::nest();
1820
        Config::inst()->update('SSViewer', 'theme_enabled', true);
1821
        $html = $arrayData->renderWith($emailClassName);
1822
        Config::unnest();
1823
1824
        return Order_Email::emogrify_html($html);
1825
    }
1826
1827
/*******************************************************
1828
   * 6. ITEM MANAGEMENT
1829
*******************************************************/
1830
1831
    /**
1832
     * returns a list of Order Attributes by type.
1833
     *
1834
     * @param array | String $types
1835
     *
1836
     * @return ArrayList
1837
     */
1838
    public function getOrderAttributesByType($types)
1839
    {
1840
        if (!is_array($types) && is_string($types)) {
1841
            $types = array($types);
1842
        }
1843
        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...
1844
            user_error('wrong parameter (types) provided in Order::getOrderAttributesByTypes');
1845
        }
1846
        $al = new ArrayList();
1847
        $items = $this->Items();
1848
        foreach ($items as $item) {
1849
            if (in_array($item->OrderAttributeType(), $types)) {
1850
                $al->push($item);
1851
            }
1852
        }
1853
        $modifiers = $this->Modifiers();
1854
        foreach ($modifiers as $modifier) {
1855
            if (in_array($modifier->OrderAttributeType(), $types)) {
1856
                $al->push($modifier);
1857
            }
1858
        }
1859
1860
        return $al;
1861
    }
1862
1863
    /**
1864
     * Returns the items of the order.
1865
     * Items are the order items (products) and NOT the modifiers (discount, tax, etc...).
1866
     *
1867
     * N. B. this method returns Order Items
1868
     * also see Buaybles
1869
1870
     *
1871
     * @param string filter - where statement to exclude certain items OR ClassName (e.g. 'TaxModifier')
1872
     *
1873
     * @return DataList (OrderItems)
1874
     */
1875
    public function Items($filterOrClassName = '')
1876
    {
1877
        if (!$this->exists()) {
1878
            $this->write();
1879
        }
1880
1881
        return $this->itemsFromDatabase($filterOrClassName);
1882
    }
1883
1884
    /**
1885
     * @alias function of Items
1886
     *
1887
     * N. B. this method returns Order Items
1888
     * also see Buaybles
1889
     *
1890
     * @param string filter - where statement to exclude certain items.
1891
     * @alias for Items
1892
     * @return DataList (OrderItems)
1893
     */
1894
    public function OrderItems($filterOrClassName = '')
1895
    {
1896
        return $this->Items($filterOrClassName);
1897
    }
1898
1899
    /**
1900
     * returns the buyables asscoiated with the order items.
1901
     *
1902
     * NB. this method retursn buyables
1903
     *
1904
     * @param string filter - where statement to exclude certain items.
1905
     *
1906
     * @return ArrayList (Buyables)
1907
     */
1908
    public function Buyables($filterOrClassName = '')
1909
    {
1910
        $items = $this->Items($filterOrClassName);
1911
        $arrayList = new ArrayList();
1912
        foreach ($items as $item) {
1913
            $arrayList->push($item->Buyable());
1914
        }
1915
1916
        return $arrayList;
1917
    }
1918
1919
    /**
1920
     * Return all the {@link OrderItem} instances that are
1921
     * available as records in the database.
1922
     *
1923
     * @param string filter - where statement to exclude certain items,
1924
     *   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)
1925
     *
1926
     * @return DataList (OrderItems)
1927
     */
1928
    protected function itemsFromDatabase($filterOrClassName = '')
1929
    {
1930
        $className = 'OrderItem';
1931
        $extrafilter = '';
1932
        if ($filterOrClassName) {
1933
            if (class_exists($filterOrClassName)) {
1934
                $className = $filterOrClassName;
1935
            } else {
1936
                $extrafilter = " AND $filterOrClassName";
1937
            }
1938
        }
1939
1940
        return $className::get()->filter(array('OrderID' => $this->ID))->where($extrafilter);
1941
    }
1942
1943
    /**
1944
     * @alias for Modifiers
1945
     *
1946
     * @return DataList (OrderModifiers)
1947
     */
1948
    public function OrderModifiers()
1949
    {
1950
        return $this->Modifiers();
1951
    }
1952
1953
    /**
1954
     * Returns the modifiers of the order, if it hasn't been saved yet
1955
     * it returns the modifiers from session, if it has, it returns them
1956
     * from the DB entry. ONLY USE OUTSIDE ORDER.
1957
     *
1958
     * @param string filter - where statement to exclude certain items OR ClassName (e.g. 'TaxModifier')
1959
     *
1960
     * @return DataList (OrderModifiers)
1961
     */
1962
    public function Modifiers($filterOrClassName = '')
1963
    {
1964
        return $this->modifiersFromDatabase($filterOrClassName);
1965
    }
1966
1967
    /**
1968
     * Get all {@link OrderModifier} instances that are
1969
     * available as records in the database.
1970
     * NOTE: includes REMOVED Modifiers, so that they do not get added again...
1971
     *
1972
     * @param string filter - where statement to exclude certain items OR ClassName (e.g. 'TaxModifier')
1973
     *
1974
     * @return DataList (OrderModifiers)
1975
     */
1976
    protected function modifiersFromDatabase($filterOrClassName = '')
1977
    {
1978
        $className = 'OrderModifier';
1979
        $extrafilter = '';
1980
        if ($filterOrClassName) {
1981
            if (class_exists($filterOrClassName)) {
1982
                $className = $filterOrClassName;
1983
            } else {
1984
                $extrafilter = " AND $filterOrClassName";
1985
            }
1986
        }
1987
1988
        return $className::get()->where('"OrderAttribute"."OrderID" = '.$this->ID." $extrafilter");
1989
    }
1990
1991
    /**
1992
     * Calculates and updates all the order attributes.
1993
     *
1994
     * @param bool $recalculate - run it, even if it has run already
1995
     */
1996
    public function calculateOrderAttributes($recalculate = false)
1997
    {
1998
        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...
1999
            //submitted orders are NEVER recalculated.
2000
            //they are set in stone.
2001
        } elseif (self::get_needs_recalculating($this->ID) || $recalculate) {
2002
            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...
2003
                $this->ensureCorrectExchangeRate();
2004
                $this->calculateOrderItems($recalculate);
2005
                $this->calculateModifiers($recalculate);
2006
                $this->extend('onCalculateOrder');
2007
            }
2008
        }
2009
    }
2010
2011
    /**
2012
     * Calculates and updates all the product items.
2013
     *
2014
     * @param bool $recalculate - run it, even if it has run already
2015
     */
2016
    protected function calculateOrderItems($recalculate = false)
2017
    {
2018
        //check if order has modifiers already
2019
        //check /re-add all non-removable ones
2020
        //$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...
2021
        $orderItems = $this->itemsFromDatabase();
2022
        if ($orderItems->count()) {
2023
            foreach ($orderItems as $orderItem) {
2024
                if ($orderItem) {
2025
                    $orderItem->runUpdate($recalculate);
2026
                }
2027
            }
2028
        }
2029
        $this->extend('onCalculateOrderItems', $orderItems);
2030
    }
2031
2032
    /**
2033
     * Calculates and updates all the modifiers.
2034
     *
2035
     * @param bool $recalculate - run it, even if it has run already
2036
     */
2037
    protected function calculateModifiers($recalculate = false)
2038
    {
2039
        $createdModifiers = $this->modifiersFromDatabase();
2040
        if ($createdModifiers->count()) {
2041
            foreach ($createdModifiers as $modifier) {
2042
                if ($modifier) {
2043
                    $modifier->runUpdate($recalculate);
2044
                }
2045
            }
2046
        }
2047
        $this->extend('onCalculateModifiers', $createdModifiers);
2048
    }
2049
2050
    /**
2051
     * Returns the subtotal of the modifiers for this order.
2052
     * If a modifier appears in the excludedModifiers array, it is not counted.
2053
     *
2054
     * @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...
2055
     * @param bool         $stopAtExcludedModifier - when this flag is TRUE, we stop adding the modifiers when we reach an excluded modifier.
2056
     *
2057
     * @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...
2058
     */
2059
    public function ModifiersSubTotal($excluded = null, $stopAtExcludedModifier = false)
2060
    {
2061
        $total = 0;
2062
        $modifiers = $this->Modifiers();
2063
        if ($modifiers->count()) {
2064
            foreach ($modifiers as $modifier) {
2065
                if (!$modifier->IsRemoved()) { //we just double-check this...
2066
                    if (is_array($excluded) && in_array($modifier->ClassName, $excluded)) {
2067
                        if ($stopAtExcludedModifier) {
2068
                            break;
2069
                        }
2070
                        //do the next modifier
2071
                        continue;
2072
                    } elseif (is_string($excluded) && ($modifier->ClassName == $excluded)) {
2073
                        if ($stopAtExcludedModifier) {
2074
                            break;
2075
                        }
2076
                        //do the next modifier
2077
                        continue;
2078
                    }
2079
                    $total += $modifier->CalculationTotal();
2080
                }
2081
            }
2082
        }
2083
2084
        return $total;
2085
    }
2086
2087
    /**
2088
     * returns a modifier that is an instanceof the classname
2089
     * it extends.
2090
     *
2091
     * @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...
2092
     *
2093
     * @return DataObject (OrderModifier)
2094
     **/
2095
    public function RetrieveModifier($className)
2096
    {
2097
        $modifiers = $this->Modifiers();
2098
        if ($modifiers->count()) {
2099
            foreach ($modifiers as $modifier) {
2100
                if (is_a($modifier, Object::getCustomClass($className))) {
2101
                    return $modifier;
2102
                }
2103
            }
2104
        }
2105
    }
2106
2107
/*******************************************************
2108
   * 7. CRUD METHODS (e.g. canView, canEdit, canDelete, etc...)
2109
*******************************************************/
2110
2111
    /**
2112
     * @param Member $member
2113
     *
2114
     * @return DataObject (Member)
2115
     **/
2116
     //TODO: please comment why we make use of this function
2117
    protected function getMemberForCanFunctions(Member $member = null)
2118
    {
2119
        if (!$member) {
2120
            $member = Member::currentUser();
2121
        }
2122
        if (!$member) {
2123
            $member = new Member();
2124
            $member->ID = 0;
2125
        }
2126
2127
        return $member;
2128
    }
2129
2130
    /**
2131
     * @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...
2132
     *
2133
     * @return bool
2134
     **/
2135
    public function canCreate($member = null)
2136
    {
2137
        $member = $this->getMemberForCanFunctions($member);
2138
        $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...
2139
        if ($extended !== null) {
2140
            return $extended;
2141
        }
2142
        if ($member->exists()) {
2143
            return $member->IsShopAdmin();
2144
        }
2145
    }
2146
2147
    /**
2148
     * Standard SS method - can the current member view this order?
2149
     *
2150
     * @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...
2151
     *
2152
     * @return bool
2153
     **/
2154
    public function canView($member = null)
2155
    {
2156
        if (!$this->exists()) {
2157
            return true;
2158
        }
2159
        $member = $this->getMemberForCanFunctions($member);
2160
        //check if this has been "altered" in any DataExtension
2161
        $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...
2162
        if ($extended !== null) {
2163
            return $extended;
2164
        }
2165
        //is the member is a shop admin they can always view it
2166
        if (EcommerceRole::current_member_is_shop_admin($member)) {
2167
            return true;
2168
        }
2169
2170
        //is the member is a shop assistant they can always view it
2171
        if (EcommerceRole::current_member_is_shop_assistant($member)) {
2172
            return true;
2173
        }
2174
        //if the current member OWNS the order, (s)he can always view it.
2175
        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...
2176
            return true;
2177
        }
2178
        //it is the current order
2179
        if ($this->IsInSession()) {
2180
            //we do some additional CHECKS for session hackings!
2181
            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...
2182
                //can't view the order of another member!
2183
                //shop admin exemption is already captured.
2184
                //this is always true
2185
                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...
2186
                    return false;
2187
                }
2188
            } else {
2189
                //order belongs to someone, but current user is NOT logged in...
2190
                //this is allowed!
2191
                //the reason it is allowed is because we want to be able to
2192
                //add order to non-existing member
2193
                return true;
2194
            }
2195
        }
2196
2197
        return false;
2198
    }
2199
2200
    /**
2201
     * @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...
2202
     * @return bool
2203
     */
2204
    public function canOverrideCanView($member = null)
2205
    {
2206
        if ($this->canView($member)) {
2207
            //can view overrides any concerns
2208
            return true;
2209
        } else {
2210
            $tsOrder = strtotime($this->LastEdited);
2211
            $tsNow = time();
2212
            $minutes = EcommerceConfig::get('Order', 'minutes_an_order_can_be_viewed_without_logging_in');
2213
            if ($minutes && ((($tsNow - $tsOrder) / 60) < $minutes)) {
2214
2215
                //has the order been edited recently?
2216
                return true;
2217
            } elseif ($orderStep = $this->MyStep()) {
2218
2219
                // order is being processed ...
2220
                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...
2221
            }
2222
        }
2223
        return false;
2224
    }
2225
2226
    /**
2227
     * @return bool
2228
     */
2229
    public function IsInSession()
2230
    {
2231
        $orderInSession = ShoppingCart::session_order();
2232
2233
        return $orderInSession && $this->ID && $this->ID == $orderInSession->ID;
2234
    }
2235
2236
    /**
2237
     * returns a pseudo random part of the session id.
2238
     *
2239
     * @param int $size
2240
     *
2241
     * @return string
2242
     */
2243
    public function LessSecureSessionID($size = 7, $start = null)
2244
    {
2245
        if (!$start || $start < 0 || $start > (32 - $size)) {
2246
            $start = 0;
2247
        }
2248
2249
        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...
2250
    }
2251
    /**
2252
     *
2253
     * @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...
2254
     *
2255
     * @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...
2256
     **/
2257
    public function canViewAdminStuff($member = null)
2258
    {
2259
        $member = $this->getMemberForCanFunctions($member);
2260
        $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...
2261
        if ($extended !== null) {
2262
            return $extended;
2263
        }
2264
        if (Permission::checkMember($member, Config::inst()->get('EcommerceRole', 'admin_permission_code'))) {
2265
            return true;
2266
        }
2267
    }
2268
2269
    /**
2270
     * if we set canEdit to false then we
2271
     * can not see the child records
2272
     * Basically, you can edit when you can view and canEdit (even as a customer)
2273
     * Or if you are a Shop Admin you can always edit.
2274
     * Otherwise it is false...
2275
     *
2276
     * @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...
2277
     *
2278
     * @return bool
2279
     **/
2280
    public function canEdit($member = null)
2281
    {
2282
        $member = $this->getMemberForCanFunctions($member);
2283
        $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...
2284
        if ($extended !== null) {
2285
            return $extended;
2286
        }
2287
        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...
2288
            return true;
2289
        }
2290
        if (Permission::checkMember($member, Config::inst()->get('EcommerceRole', 'admin_permission_code'))) {
2291
            return true;
2292
        }
2293
        //is the member is a shop assistant they can always view it
2294
        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...
2295
            return true;
2296
        }
2297
        return false;
2298
    }
2299
2300
    /**
2301
     * is the order ready to go through to the
2302
     * checkout process.
2303
     *
2304
     * This method checks all the order items and order modifiers
2305
     * If any of them need immediate attention then this is done
2306
     * first after which it will go through to the checkout page.
2307
     *
2308
     * @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...
2309
     *
2310
     * @return bool
2311
     **/
2312
    public function canCheckout(Member $member = null)
2313
    {
2314
        $member = $this->getMemberForCanFunctions($member);
2315
        $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...
2316
        if ($extended !== null) {
2317
            return $extended;
2318
        }
2319
        $submitErrors = $this->SubmitErrors();
2320
        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...
2321
            return false;
2322
        }
2323
2324
        return true;
2325
    }
2326
2327
    /**
2328
     * Can the order be submitted?
2329
     * this method can be used to stop an order from being submitted
2330
     * due to something not being completed or done.
2331
     *
2332
     * @see Order::SubmitErrors
2333
     *
2334
     * @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...
2335
     *
2336
     * @return bool
2337
     **/
2338
    public function canSubmit(Member $member = null)
2339
    {
2340
        $member = $this->getMemberForCanFunctions($member);
2341
        $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...
2342
        if ($extended !== null) {
2343
            return $extended;
2344
        }
2345
        if ($this->IsSubmitted()) {
2346
            return false;
2347
        }
2348
        $submitErrors = $this->SubmitErrors();
2349
        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...
2350
            return false;
2351
        }
2352
2353
        return true;
2354
    }
2355
2356
    /**
2357
     * Can a payment be made for this Order?
2358
     *
2359
     * @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...
2360
     *
2361
     * @return bool
2362
     **/
2363
    public function canPay(Member $member = null)
2364
    {
2365
        $member = $this->getMemberForCanFunctions($member);
2366
        $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...
2367
        if ($extended !== null) {
2368
            return $extended;
2369
        }
2370
        if ($this->IsPaid() || $this->IsCancelled() || $this->PaymentIsPending()) {
2371
            return false;
2372
        }
2373
2374
        return $this->MyStep()->CustomerCanPay;
2375
    }
2376
2377
    /**
2378
     * Can the given member cancel this order?
2379
     *
2380
     * @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...
2381
     *
2382
     * @return bool
2383
     **/
2384
    public function canCancel(Member $member = null)
2385
    {
2386
        //if it is already cancelled it can not be cancelled again
2387
        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...
2388
            return false;
2389
        }
2390
        $member = $this->getMemberForCanFunctions($member);
2391
        $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...
2392
        if ($extended !== null) {
2393
            return $extended;
2394
        }
2395
        if (EcommerceRole::current_member_can_process_orders($member)) {
2396
            return true;
2397
        }
2398
2399
        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...
2400
    }
2401
2402
    /**
2403
     * @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...
2404
     *
2405
     * @return bool
2406
     **/
2407
    public function canDelete($member = null)
2408
    {
2409
        $member = $this->getMemberForCanFunctions($member);
2410
        $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...
2411
        if ($extended !== null) {
2412
            return $extended;
2413
        }
2414
        if ($this->IsSubmitted()) {
2415
            return false;
2416
        }
2417
        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...
2418
            return true;
2419
        }
2420
2421
        return false;
2422
    }
2423
2424
    /**
2425
     * Returns all the order logs that the current member can view
2426
     * i.e. some order logs can only be viewed by the admin (e.g. suspected fraud orderlog).
2427
     *
2428
     * @return ArrayList (OrderStatusLogs)
2429
     **/
2430
    public function CanViewOrderStatusLogs()
2431
    {
2432
        $canViewOrderStatusLogs = new ArrayList();
2433
        $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...
2434
        foreach ($logs as $log) {
2435
            if ($log->canView()) {
2436
                $canViewOrderStatusLogs->push($log);
2437
            }
2438
        }
2439
2440
        return $canViewOrderStatusLogs;
2441
    }
2442
2443
    /**
2444
     * returns all the logs that can be viewed by the customer.
2445
     *
2446
     * @return ArrayList (OrderStausLogs)
2447
     */
2448
    public function CustomerViewableOrderStatusLogs()
2449
    {
2450
        $customerViewableOrderStatusLogs = new ArrayList();
2451
        $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...
2452
        if ($logs) {
2453
            foreach ($logs as $log) {
2454
                if (!$log->InternalUseOnly) {
2455
                    $customerViewableOrderStatusLogs->push($log);
2456
                }
2457
            }
2458
        }
2459
2460
        return $customerViewableOrderStatusLogs;
2461
    }
2462
2463
/*******************************************************
2464
   * 8. GET METHODS (e.g. Total, SubTotal, Title, etc...)
2465
*******************************************************/
2466
2467
    /**
2468
     * returns the email to be used for customer communication.
2469
     *
2470
     * @return string
2471
     */
2472
    public function OrderEmail()
2473
    {
2474
        return $this->getOrderEmail();
2475
    }
2476
    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...
2477
    {
2478
        $email = '';
2479
        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...
2480
            $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...
2481
        }
2482
        if (! $email) {
2483
            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...
2484
                $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...
2485
            }
2486
        }
2487
        $extendedEmail = $this->extend('updateOrderEmail', $email);
2488
        if ($extendedEmail !== null && is_array($extendedEmail) && count($extendedEmail)) {
2489
            $email = implode(';', $extendedEmail);
2490
        }
2491
2492
        return $email;
2493
    }
2494
2495
    /**
2496
     * Returns true if there is a prink or email link.
2497
     *
2498
     * @return bool
2499
     */
2500
    public function HasPrintOrEmailLink()
2501
    {
2502
        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...
2503
    }
2504
2505
    /**
2506
     * returns the absolute link to the order that can be used in the customer communication (email).
2507
     *
2508
     * @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...
2509
     */
2510
    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...
2511
    {
2512
        return $this->getEmailLink();
2513
    }
2514
    public function getEmailLink($type = 'Order_StatusEmail')
0 ignored issues
show
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...
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...
2515
    {
2516
        if (!isset($_REQUEST['print'])) {
2517
            if ($this->IsSubmitted()) {
2518
                return Director::AbsoluteURL(OrderConfirmationPage::get_email_link($this->ID, $this->MyStep()->getEmailClassName(), $actuallySendEmail = true));
2519
            }
2520
        }
2521
    }
2522
2523
    /**
2524
     * returns the absolute link to the order for printing.
2525
     *
2526
     * @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...
2527
     */
2528
    public function PrintLink()
2529
    {
2530
        return $this->getPrintLink();
2531
    }
2532
    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...
2533
    {
2534
        if (!isset($_REQUEST['print'])) {
2535
            if ($this->IsSubmitted()) {
2536
                return Director::AbsoluteURL(OrderConfirmationPage::get_order_link($this->ID)).'?print=1';
2537
            }
2538
        }
2539
    }
2540
2541
    /**
2542
     * returns the absolute link to the order for printing.
2543
     *
2544
     * @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...
2545
     */
2546
    public function PackingSlipLink()
2547
    {
2548
        return $this->getPackingSlipLink();
2549
    }
2550
    public function getPackingSlipLink()
2551
    {
2552
        if ($this->IsSubmitted()) {
2553
            return Director::AbsoluteURL(OrderConfirmationPage::get_order_link($this->ID)).'?packingslip=1';
2554
        }
2555
    }
2556
2557
    /**
2558
     * returns the absolute link that the customer can use to retrieve the email WITHOUT logging in.
2559
     *
2560
     * @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...
2561
     */
2562
    public function RetrieveLink()
2563
    {
2564
        return $this->getRetrieveLink();
2565
    }
2566
2567
    public function getRetrieveLink()
2568
    {
2569
        //important to recalculate!
2570
        if ($this->IsSubmitted($recalculate = true)) {
2571
            //add session ID if not added yet...
2572
            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...
2573
                $this->write();
2574
            }
2575
2576
            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...
2577
        } else {
2578
            return Director::AbsoluteURL('/shoppingcart/loadorder/'.$this->ID.'/');
2579
        }
2580
    }
2581
2582
    public function ShareLink()
2583
    {
2584
        return $this->getShareLink();
2585
    }
2586
2587
    public function getShareLink()
2588
    {
2589
        $orderItems = $this->itemsFromDatabase();
2590
        $action = 'share';
2591
        $array = array();
2592
        foreach ($orderItems as $orderItem) {
2593
            $array[] = implode(
2594
                ',',
2595
                array(
2596
                    $orderItem->BuyableClassName,
2597
                    $orderItem->BuyableID,
2598
                    $orderItem->Quantity
2599
                )
2600
            );
2601
        }
2602
2603
        return Director::AbsoluteURL(CartPage::find_link($action.'/'.implode('-', $array)));
2604
    }
2605
2606
    /**
2607
     * link to delete order.
2608
     *
2609
     * @return string
2610
     */
2611
    public function DeleteLink()
2612
    {
2613
        return $this->getDeleteLink();
2614
    }
2615
    public function getDeleteLink()
2616
    {
2617
        if ($this->canDelete()) {
2618
            return ShoppingCart_Controller::delete_order_link($this->ID);
2619
        } else {
2620
            return '';
2621
        }
2622
    }
2623
2624
    /**
2625
     * link to copy order.
2626
     *
2627
     * @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...
2628
     */
2629
    public function CopyOrderLink()
2630
    {
2631
        return $this->getCopyOrderLink();
2632
    }
2633
    public function getCopyOrderLink()
2634
    {
2635
        if ($this->canView() && $this->IsSubmitted()) {
2636
            return ShoppingCart_Controller::copy_order_link($this->ID);
2637
        } else {
2638
            return '';
2639
        }
2640
    }
2641
2642
    /**
2643
     * A "Title" for the order, which summarises the main details (date, and customer) in a string.
2644
     *
2645
     * @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...
2646
     * @param bool   $includeName - e.g. by Mr Johnson
2647
     *
2648
     * @return string
2649
     **/
2650
    public function Title($dateFormat = null, $includeName = false)
2651
    {
2652
        return $this->getTitle($dateFormat, $includeName);
2653
    }
2654
    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...
2655
    {
2656
        if ($this->exists()) {
2657
            if ($dateFormat === null) {
2658
                $dateFormat = EcommerceConfig::get('Order', 'date_format_for_title');
2659
            }
2660
            if ($includeName === null) {
2661
                $includeName = EcommerceConfig::get('Order', 'include_customer_name_in_title');
2662
            }
2663
            $title = $this->i18n_singular_name()." #$this->ID";
2664
            if ($dateFormat) {
2665
                if ($submissionLog = $this->SubmissionLog()) {
2666
                    $dateObject = $submissionLog->dbObject('Created');
2667
                    $placed = _t('Order.PLACED', 'placed');
2668
                } else {
2669
                    $dateObject = $this->dbObject('Created');
2670
                    $placed = _t('Order.STARTED', 'started');
2671
                }
2672
                $title .= ', '.$placed.' '.$dateObject->Format($dateFormat);
2673
            }
2674
            $name = '';
2675
            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...
2676
                $name = ' - '._t('Order.CANCELLED', 'CANCELLED');
2677
            }
2678
            if ($includeName) {
2679
                $by = _t('Order.BY', 'by');
2680
                if (!$name) {
2681
                    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...
2682
                        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...
2683
                            $name = ' - '.$by.' '.$billingAddress->Prefix.' '.$billingAddress->FirstName.' '.$billingAddress->Surname;
2684
                        }
2685
                    }
2686
                }
2687
                if (!$name) {
2688
                    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...
2689
                        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...
2690
                            if ($member->exists()) {
2691
                                if ($memberName = $member->getName()) {
2692
                                    if (!trim($memberName)) {
2693
                                        $memberName = _t('Order.ANONYMOUS', 'anonymous');
2694
                                    }
2695
                                    $name = ' - '.$by.' '.$memberName;
2696
                                }
2697
                            }
2698
                        }
2699
                    }
2700
                }
2701
            }
2702
            $title .= $name;
2703
        } else {
2704
            $title = _t('Order.NEW', 'New').' '.$this->i18n_singular_name();
2705
        }
2706
        $extendedTitle = $this->extend('updateTitle', $title);
2707
        if ($extendedTitle !== null && is_array($extendedTitle) && count($extendedTitle)) {
2708
            $title = implode('; ', $extendedTitle);
2709
        }
2710
2711
        return $title;
2712
    }
2713
2714
    /**
2715
     * Returns the subtotal of the items for this order.
2716
     *
2717
     * @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...
2718
     */
2719
    public function SubTotal()
2720
    {
2721
        return $this->getSubTotal();
2722
    }
2723
    public function getSubTotal()
2724
    {
2725
        $result = 0;
2726
        $items = $this->Items();
2727
        if ($items->count()) {
2728
            foreach ($items as $item) {
2729
                if (is_a($item, Object::getCustomClass('OrderAttribute'))) {
2730
                    $result += $item->Total();
2731
                }
2732
            }
2733
        }
2734
2735
        return $result;
2736
    }
2737
2738
    /**
2739
     * @return Currency (DB Object)
2740
     **/
2741
    public function SubTotalAsCurrencyObject()
2742
    {
2743
        return DBField::create_field('Currency', $this->SubTotal());
2744
    }
2745
2746
    /**
2747
     * @return Money
2748
     **/
2749
    public function SubTotalAsMoney()
2750
    {
2751
        return $this->getSubTotalAsMoney();
2752
    }
2753
    public function getSubTotalAsMoney()
2754
    {
2755
        return EcommerceCurrency::get_money_object_from_order_currency($this->SubTotal(), $this);
2756
    }
2757
2758
    /**
2759
     * @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...
2760
     * @param bool         $stopAtExcludedModifier - when this flag is TRUE, we stop adding the modifiers when we reach an excluded modifier.
2761
     *
2762
     * @return Currency (DB Object)
2763
     **/
2764
    public function ModifiersSubTotalAsCurrencyObject($excluded = null, $stopAtExcludedModifier = false)
2765
    {
2766
        return DBField::create_field('Currency', $this->ModifiersSubTotal($excluded, $stopAtExcludedModifier));
2767
    }
2768
2769
    /**
2770
     * @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...
2771
     * @param bool         $stopAtExcludedModifier - when this flag is TRUE, we stop adding the modifiers when we reach an excluded modifier.
2772
     *
2773
     * @return Money (DB Object)
2774
     **/
2775
    public function ModifiersSubTotalAsMoneyObject($excluded = null, $stopAtExcludedModifier = false)
2776
    {
2777
        return EcommerceCurrency::get_money_object_from_order_currency($this->ModifiersSubTotal($excluded, $stopAtExcludedModifier), $this);
2778
    }
2779
2780
    /**
2781
     * Returns the total cost of an order including the additional charges or deductions of its modifiers.
2782
     *
2783
     * @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...
2784
     */
2785
    public function Total()
2786
    {
2787
        return $this->getTotal();
2788
    }
2789
    public function getTotal()
2790
    {
2791
        return $this->SubTotal() + $this->ModifiersSubTotal();
2792
    }
2793
2794
    /**
2795
     * @return Currency (DB Object)
2796
     **/
2797
    public function TotalAsCurrencyObject()
2798
    {
2799
        return DBField::create_field('Currency', $this->Total());
2800
    }
2801
2802
    /**
2803
     * @return Money
2804
     **/
2805
    public function TotalAsMoney()
2806
    {
2807
        return $this->getTotalAsMoney();
2808
    }
2809
    public function getTotalAsMoney()
2810
    {
2811
        return EcommerceCurrency::get_money_object_from_order_currency($this->Total(), $this);
2812
    }
2813
2814
    /**
2815
     * Checks to see if any payments have been made on this order
2816
     * and if so, subracts the payment amount from the order.
2817
     *
2818
     * @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...
2819
     **/
2820
    public function TotalOutstanding()
2821
    {
2822
        return $this->getTotalOutstanding();
2823
    }
2824
    public function getTotalOutstanding()
2825
    {
2826
        if ($this->IsSubmitted()) {
2827
            $total = $this->Total();
2828
            $paid = $this->TotalPaid();
2829
            $outstanding = $total - $paid;
2830
            $maxDifference = EcommerceConfig::get('Order', 'maximum_ignorable_sales_payments_difference');
2831
            if (abs($outstanding) < $maxDifference) {
2832
                $outstanding = 0;
2833
            }
2834
2835
            return floatval($outstanding);
2836
        } else {
2837
            return 0;
2838
        }
2839
    }
2840
2841
    /**
2842
     * @return Currency (DB Object)
2843
     **/
2844
    public function TotalOutstandingAsCurrencyObject()
2845
    {
2846
        return DBField::create_field('Currency', $this->TotalOutstanding());
2847
    }
2848
2849
    /**
2850
     * @return Money
2851
     **/
2852
    public function TotalOutstandingAsMoney()
2853
    {
2854
        return $this->getTotalOutstandingAsMoney();
2855
    }
2856
    public function getTotalOutstandingAsMoney()
2857
    {
2858
        return EcommerceCurrency::get_money_object_from_order_currency($this->TotalOutstanding(), $this);
2859
    }
2860
2861
    /**
2862
     * @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...
2863
     */
2864
    public function TotalPaid()
2865
    {
2866
        return $this->getTotalPaid();
2867
    }
2868
    public function getTotalPaid()
2869
    {
2870
        $paid = 0;
2871
        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...
2872
            foreach ($payments as $payment) {
2873
                if ($payment->Status == 'Success') {
2874
                    $paid += $payment->Amount->getAmount();
2875
                }
2876
            }
2877
        }
2878
        $reverseExchange = 1;
2879
        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...
2880
            $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...
2881
        }
2882
2883
        return $paid * $reverseExchange;
2884
    }
2885
2886
    /**
2887
     * @return Currency (DB Object)
2888
     **/
2889
    public function TotalPaidAsCurrencyObject()
2890
    {
2891
        return DBField::create_field('Currency', $this->TotalPaid());
2892
    }
2893
2894
    /**
2895
     * @return Money
2896
     **/
2897
    public function TotalPaidAsMoney()
2898
    {
2899
        return $this->getTotalPaidAsMoney();
2900
    }
2901
    public function getTotalPaidAsMoney()
2902
    {
2903
        return EcommerceCurrency::get_money_object_from_order_currency($this->TotalPaid(), $this);
2904
    }
2905
2906
    /**
2907
     * returns the total number of OrderItems (not modifiers).
2908
     * This is meant to run as fast as possible to quickly check
2909
     * if there is anything in the cart.
2910
     *
2911
     * @param bool $recalculate - do we need to recalculate (value is retained during lifetime of Object)
2912
     *
2913
     * @return int
2914
     **/
2915
    public function TotalItems($recalculate = false)
2916
    {
2917
        return $this->getTotalItems($recalculate);
2918
    }
2919
    public function getTotalItems($recalculate = false)
2920
    {
2921
        if ($this->totalItems === null || $recalculate) {
2922
            $this->totalItems = OrderItem::get()
2923
                ->where('"OrderAttribute"."OrderID" = '.$this->ID.' AND "OrderItem"."Quantity" > 0')
2924
                ->count();
2925
        }
2926
2927
        return $this->totalItems;
2928
    }
2929
2930
    /**
2931
     * Little shorthand.
2932
     *
2933
     * @param bool $recalculate
2934
     *
2935
     * @return bool
2936
     **/
2937
    public function MoreThanOneItemInCart($recalculate = false)
2938
    {
2939
        return $this->TotalItems($recalculate) > 1 ? true : false;
2940
    }
2941
2942
    /**
2943
     * returns the total number of OrderItems (not modifiers) times their respectective quantities.
2944
     *
2945
     * @param bool $recalculate - force recalculation
2946
     *
2947
     * @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...
2948
     **/
2949
    public function TotalItemsTimesQuantity($recalculate = false)
2950
    {
2951
        return $this->getTotalItemsTimesQuantity($recalculate);
2952
    }
2953
    public function getTotalItemsTimesQuantity($recalculate = false)
2954
    {
2955
        if ($this->totalItemsTimesQuantity === null || $recalculate) {
2956
            //to do, why do we check if you can edit ????
2957
            $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...
2958
                SELECT SUM("OrderItem"."Quantity")
2959
                FROM "OrderItem"
2960
                    INNER JOIN "OrderAttribute" ON "OrderAttribute"."ID" = "OrderItem"."ID"
2961
                WHERE
2962
                    "OrderAttribute"."OrderID" = '.$this->ID.'
2963
                    AND "OrderItem"."Quantity" > 0'
2964
            )->value();
2965
        }
2966
2967
        return $this->totalItemsTimesQuantity - 0;
2968
    }
2969
2970
    /**
2971
     *
2972
     * @return string (country code)
2973
     **/
2974
    public function Country()
2975
    {
2976
        return $this->getCountry();
2977
    }
2978
2979
    /**
2980
    * Returns the country code for the country that applies to the order.
2981
    * @alias  for getCountry
2982
    *
2983
    * @return string - country code e.g. NZ
2984
     */
2985
    public function getCountry()
2986
    {
2987
        $countryCodes = array(
2988
            'Billing' => '',
2989
            'Shipping' => '',
2990
        );
2991
        $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...
2992
        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...
2993
            $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...
2994
            if ($billingAddress) {
2995
                if ($billingAddress->Country) {
2996
                    $countryCodes['Billing'] = $billingAddress->Country;
2997
                }
2998
            }
2999
        }
3000
        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...
3001
            $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...
3002
            if ($shippingAddress) {
3003
                if ($shippingAddress->ShippingCountry) {
3004
                    $countryCodes['Shipping'] = $shippingAddress->ShippingCountry;
3005
                }
3006
            }
3007
        }
3008
        if (
3009
            (EcommerceConfig::get('OrderAddress', 'use_shipping_address_for_main_region_and_country') && $countryCodes['Shipping'])
3010
            ||
3011
            (!$countryCodes['Billing'] && $countryCodes['Shipping'])
3012
        ) {
3013
            $code = $countryCodes['Shipping'];
3014
        } elseif ($countryCodes['Billing']) {
3015
            $code = $countryCodes['Billing'];
3016
        } else {
3017
            $code = EcommerceCountry::get_country_from_ip();
3018
        }
3019
3020
        return $code;
3021
    }
3022
3023
    /**
3024
     * @alias for getFullNameCountry
3025
     *
3026
     * @return string - country name
3027
     **/
3028
    public function FullNameCountry()
3029
    {
3030
        return $this->getFullNameCountry();
3031
    }
3032
3033
    /**
3034
     * returns name of coutry.
3035
     *
3036
     * @return string - country name
3037
     **/
3038
    public function getFullNameCountry()
3039
    {
3040
        return EcommerceCountry::find_title($this->Country());
3041
    }
3042
3043
    /**
3044
     * @alis for getExpectedCountryName
3045
     * @return string - country name
3046
     **/
3047
    public function ExpectedCountryName()
3048
    {
3049
        return $this->getExpectedCountryName();
3050
    }
3051
3052
    /**
3053
     * returns name of coutry that we expect the customer to have
3054
     * This takes into consideration more than just what has been entered
3055
     * for example, it looks at GEO IP.
3056
     *
3057
     * @todo: why do we dont return a string IF there is only one item.
3058
     *
3059
     * @return string - country name
3060
     **/
3061
    public function getExpectedCountryName()
3062
    {
3063
        return EcommerceCountry::find_title(EcommerceCountry::get_country(false, $this->ID));
3064
    }
3065
3066
    /**
3067
     * return the title of the fixed country (if any).
3068
     *
3069
     * @return string | empty string
3070
     **/
3071
    public function FixedCountry()
3072
    {
3073
        return $this->getFixedCountry();
3074
    }
3075
    public function getFixedCountry()
3076
    {
3077
        $code = EcommerceCountry::get_fixed_country_code();
3078
        if ($code) {
3079
            return EcommerceCountry::find_title($code);
3080
        }
3081
3082
        return '';
3083
    }
3084
3085
    /**
3086
     * Returns the region that applies to the order.
3087
     * we check both billing and shipping, in case one of them is empty.
3088
     *
3089
     * @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...
3090
     **/
3091
    public function Region()
3092
    {
3093
        return $this->getRegion();
3094
    }
3095
    public function getRegion()
3096
    {
3097
        $regionIDs = array(
3098
            'Billing' => 0,
3099
            'Shipping' => 0,
3100
        );
3101
        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...
3102
            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...
3103
                if ($billingAddress->RegionID) {
3104
                    $regionIDs['Billing'] = $billingAddress->RegionID;
3105
                }
3106
            }
3107
        }
3108
        if ($this->CanHaveShippingAddress()) {
3109
            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...
3110
                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...
3111
                    if ($shippingAddress->ShippingRegionID) {
3112
                        $regionIDs['Shipping'] = $shippingAddress->ShippingRegionID;
3113
                    }
3114
                }
3115
            }
3116
        }
3117
        if (count($regionIDs)) {
3118
            //note the double-check with $this->CanHaveShippingAddress() and get_use_....
3119
            if ($this->CanHaveShippingAddress() && EcommerceConfig::get('OrderAddress', 'use_shipping_address_for_main_region_and_country') && $regionIDs['Shipping']) {
3120
                return EcommerceRegion::get()->byID($regionIDs['Shipping']);
3121
            } else {
3122
                return EcommerceRegion::get()->byID($regionIDs['Billing']);
3123
            }
3124
        } else {
3125
            return EcommerceRegion::get()->byID(EcommerceRegion::get_region_from_ip());
3126
        }
3127
    }
3128
3129
    /**
3130
     * Casted variable
3131
     * Currency is not the same as the standard one?
3132
     *
3133
     * @return bool
3134
     **/
3135
    public function HasAlternativeCurrency()
3136
    {
3137
        return $this->getHasAlternativeCurrency();
3138
    }
3139
    public function getHasAlternativeCurrency()
3140
    {
3141
        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...
3142
            if ($currency->IsDefault()) {
0 ignored issues
show
Coding Style introduced by
The if-else statement can be simplified to return !$currency->IsDefault();.
Loading history...
3143
                return false;
3144
            } else {
3145
                return true;
3146
            }
3147
        } else {
3148
            return false;
3149
        }
3150
    }
3151
3152
    /**
3153
     * Makes sure exchange rate is updated and maintained before order is submitted
3154
     * This method is public because it could be called from a shopping Cart Object.
3155
     **/
3156
    public function EnsureCorrectExchangeRate()
3157
    {
3158
        if (!$this->IsSubmitted()) {
3159
            $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...
3160
            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...
3161
                if ($currency->IsDefault()) {
3162
                    $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...
3163
                } else {
3164
                    $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...
3165
                }
3166
            } else {
3167
                $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...
3168
            }
3169
            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...
3170
                $this->write();
3171
            }
3172
        }
3173
    }
3174
3175
    /**
3176
     * speeds up processing by storing the IsSubmitted value
3177
     * we start with -1 to know if it has been requested before.
3178
     *
3179
     * @var bool
3180
     */
3181
    protected $_isSubmittedTempVar = -1;
3182
3183
    /**
3184
     * Casted variable - has the order been submitted?
3185
     * alias
3186
     * @param bool $recalculate
3187
     *
3188
     * @return bool
3189
     **/
3190
    public function IsSubmitted($recalculate = true)
3191
    {
3192
        return $this->getIsSubmitted($recalculate);
3193
    }
3194
3195
    /**
3196
     * Casted variable - has the order been submitted?
3197
     *
3198
     * @param bool $recalculate
3199
     *
3200
     * @return bool
3201
     **/
3202
    public function getIsSubmitted($recalculate = false)
3203
    {
3204
        if ($this->_isSubmittedTempVar === -1 || $recalculate) {
3205
            if ($this->SubmissionLog()) {
3206
                $this->_isSubmittedTempVar = true;
3207
            } else {
3208
                $this->_isSubmittedTempVar = false;
3209
            }
3210
        }
3211
3212
        return $this->_isSubmittedTempVar;
3213
    }
3214
3215
    /**
3216
     *
3217
     *
3218
     * @return bool
3219
     */
3220
    public function IsArchived()
3221
    {
3222
        $lastStep = OrderStep::get()->Last();
3223
        if ($lastStep) {
3224
            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...
3225
                return true;
3226
            }
3227
        }
3228
        return false;
3229
    }
3230
3231
    /**
3232
     * Submission Log for this Order (if any).
3233
     *
3234
     * @return Submission Log (OrderStatusLog_Submitted) | Null
3235
     **/
3236
    public function SubmissionLog()
3237
    {
3238
        $className = EcommerceConfig::get('OrderStatusLog', 'order_status_log_class_used_for_submitting_order');
3239
3240
        return $className::get()
3241
            ->Filter(array('OrderID' => $this->ID))
3242
            ->Last();
3243
    }
3244
3245
    /**
3246
     * @return int
3247
     */
3248
    public function SecondsSinceBeingSubmitted()
3249
    {
3250
        if ($submissionLog = $this->SubmissionLog()) {
3251
            return time() - strtotime($submissionLog->Created);
3252
        } else {
3253
            return 0;
3254
        }
3255
    }
3256
3257
    /**
3258
     * if the order can not be submitted,
3259
     * then the reasons why it can not be submitted
3260
     * will be returned by this method.
3261
     *
3262
     * @see Order::canSubmit
3263
     *
3264
     * @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...
3265
     */
3266
    public function SubmitErrors()
3267
    {
3268
        $al = null;
3269
        $extendedSubmitErrors = $this->extend('updateSubmitErrors');
3270
        if ($extendedSubmitErrors !== null && is_array($extendedSubmitErrors) && count($extendedSubmitErrors)) {
3271
            $al = ArrayList::create();
3272
            foreach ($extendedSubmitErrors as $returnResultArray) {
3273
                foreach ($returnResultArray as $issue) {
3274
                    if ($issue) {
3275
                        $al->push(ArrayData::create(array("Title" => $issue)));
3276
                    }
3277
                }
3278
            }
3279
        }
3280
        return $al;
3281
    }
3282
3283
    /**
3284
     * Casted variable - has the order been submitted?
3285
     *
3286
     * @param bool $withDetail
3287
     *
3288
     * @return string
3289
     **/
3290
    public function CustomerStatus($withDetail = true)
3291
    {
3292
        return $this->getCustomerStatus($withDetail);
3293
    }
3294
    public function getCustomerStatus($withDetail = true)
3295
    {
3296
        $str = '';
3297
        if ($this->MyStep()->ShowAsUncompletedOrder) {
3298
            $str = _t('Order.UNCOMPLETED', 'Uncompleted');
3299
        } elseif ($this->MyStep()->ShowAsInProcessOrder) {
3300
            $str = _t('Order.IN_PROCESS', 'In Process');
3301
        } elseif ($this->MyStep()->ShowAsCompletedOrder) {
3302
            $str = _t('Order.COMPLETED', 'Completed');
3303
        }
3304
        if ($withDetail) {
3305
            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...
3306
                $str .= ' ('.$this->MyStep()->Name.')';
3307
            }
3308
        }
3309
3310
        return $str;
3311
    }
3312
3313
    /**
3314
     * Casted variable - does the order have a potential shipping address?
3315
     *
3316
     * @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...
3317
     **/
3318
    public function CanHaveShippingAddress()
3319
    {
3320
        return $this->getCanHaveShippingAddress();
3321
    }
3322
    public function getCanHaveShippingAddress()
3323
    {
3324
        return EcommerceConfig::get('OrderAddress', 'use_separate_shipping_address');
3325
    }
3326
3327
    /**
3328
     * returns the link to view the Order
3329
     * WHY NOT CHECKOUT PAGE: first we check for cart page.
3330
     *
3331
     * @return CartPage | Null
3332
     */
3333
    public function DisplayPage()
3334
    {
3335
        if ($this->MyStep() && $this->MyStep()->AlternativeDisplayPage()) {
3336
            $page = $this->MyStep()->AlternativeDisplayPage();
3337
        } elseif ($this->IsSubmitted()) {
3338
            $page = OrderConfirmationPage::get()->First();
3339
        } else {
3340
            $page = CartPage::get()
3341
                ->Filter(array('ClassName' => 'CartPage'))
3342
                ->First();
3343
            if (!$page) {
3344
                $page = CheckoutPage::get()->First();
3345
            }
3346
        }
3347
3348
        return $page;
3349
    }
3350
3351
    /**
3352
     * returns the link to view the Order
3353
     * WHY NOT CHECKOUT PAGE: first we check for cart page.
3354
     * If a cart page has been created then we refer through to Cart Page.
3355
     * Otherwise it will default to the checkout page.
3356
     *
3357
     * @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...
3358
     *
3359
     * @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...
3360
     */
3361
    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...
3362
    {
3363
        $page = $this->DisplayPage();
3364
        if ($page) {
3365
            return $page->getOrderLink($this->ID, $action);
3366
        } else {
3367
            user_error('A Cart / Checkout Page + an Order Confirmation Page needs to be setup for the e-commerce module to work.', E_USER_NOTICE);
3368
            $page = ErrorPage::get()
3369
                ->Filter(array('ErrorCode' => '404'))
3370
                ->First();
3371
            if ($page) {
3372
                return $page->Link();
3373
            }
3374
        }
3375
    }
3376
3377
    /**
3378
     * Returns to link to access the Order's API.
3379
     *
3380
     * @param string $version
3381
     * @param string $extension
3382
     *
3383
     * @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...
3384
     */
3385
    public function APILink($version = 'v1', $extension = 'xml')
3386
    {
3387
        return Director::AbsoluteURL("/api/ecommerce/$version/Order/".$this->ID."/.$extension");
3388
    }
3389
3390
    /**
3391
     * returns the link to finalise the Order.
3392
     *
3393
     * @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...
3394
     */
3395
    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...
3396
    {
3397
        $page = CheckoutPage::get()->First();
3398
        if ($page) {
3399
            return $page->Link();
3400
        } else {
3401
            $page = ErrorPage::get()
3402
                ->Filter(array('ErrorCode' => '404'))
3403
                ->First();
3404
            if ($page) {
3405
                return $page->Link();
3406
            }
3407
        }
3408
    }
3409
3410
    /**
3411
     * Converts the Order into HTML, based on the Order Template.
3412
     *
3413
     * @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...
3414
     **/
3415
    public function ConvertToHTML()
3416
    {
3417
        Config::nest();
3418
        Config::inst()->update('SSViewer', 'theme_enabled', true);
3419
        $html = $this->renderWith('Order');
3420
        Config::unnest();
3421
        $html = preg_replace('/(\s)+/', ' ', $html);
3422
3423
        return DBField::create_field('HTMLText', $html);
3424
    }
3425
3426
    /**
3427
     * Converts the Order into a serialized string
3428
     * TO DO: check if this works and check if we need to use special sapphire serialization code.
3429
     *
3430
     * @return string - serialized object
3431
     **/
3432
    public function ConvertToString()
3433
    {
3434
        return serialize($this->addHasOneAndHasManyAsVariables());
3435
    }
3436
3437
    /**
3438
     * Converts the Order into a JSON object
3439
     * TO DO: check if this works and check if we need to use special sapphire JSON code.
3440
     *
3441
     * @return string -  JSON
3442
     **/
3443
    public function ConvertToJSON()
3444
    {
3445
        return json_encode($this->addHasOneAndHasManyAsVariables());
3446
    }
3447
3448
    /**
3449
     * returns itself wtih more data added as variables.
3450
     * We add has_one and has_many as variables like this: $this->MyHasOne_serialized = serialize($this->MyHasOne()).
3451
     *
3452
     * @return Order - with most important has one and has many items included as variables.
3453
     **/
3454
    protected function addHasOneAndHasManyAsVariables()
3455
    {
3456
        $object = clone $this;
3457
        $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...
3458
        $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...
3459
        $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...
3460
        $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...
3461
        $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...
3462
        $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...
3463
        $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...
3464
3465
        return $object;
3466
    }
3467
3468
/*******************************************************
3469
   * 9. TEMPLATE RELATED STUFF
3470
*******************************************************/
3471
3472
    /**
3473
     * returns the instance of EcommerceConfigAjax for use in templates.
3474
     * In templates, it is used like this:
3475
     * $EcommerceConfigAjax.TableID.
3476
     *
3477
     * @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...
3478
     **/
3479
    public function AJAXDefinitions()
3480
    {
3481
        return EcommerceConfigAjax::get_one($this);
3482
    }
3483
3484
    /**
3485
     * returns the instance of EcommerceDBConfig.
3486
     *
3487
     * @return EcommerceDBConfig
3488
     **/
3489
    public function EcomConfig()
3490
    {
3491
        return EcommerceDBConfig::current_ecommerce_db_config();
3492
    }
3493
3494
    /**
3495
     * Collects the JSON data for an ajax return of the cart.
3496
     *
3497
     * @param array $js
3498
     *
3499
     * @return array (for use in AJAX for JSON)
3500
     **/
3501
    public function updateForAjax(array $js)
3502
    {
3503
        $function = EcommerceConfig::get('Order', 'ajax_subtotal_format');
3504
        if (is_array($function)) {
3505
            list($function, $format) = $function;
3506
        }
3507
        $subTotal = $this->$function();
3508
        if (isset($format)) {
3509
            $subTotal = $subTotal->$format();
3510
            unset($format);
3511
        }
3512
        $function = EcommerceConfig::get('Order', 'ajax_total_format');
3513
        if (is_array($function)) {
3514
            list($function, $format) = $function;
3515
        }
3516
        $total = $this->$function();
3517
        if (isset($format)) {
3518
            $total = $total->$format();
3519
        }
3520
        $ajaxObject = $this->AJAXDefinitions();
3521
        $js[] = array(
3522
            't' => 'id',
3523
            's' => $ajaxObject->TableSubTotalID(),
3524
            'p' => 'innerHTML',
3525
            'v' => $subTotal,
3526
        );
3527
        $js[] = array(
3528
            't' => 'id',
3529
            's' => $ajaxObject->TableTotalID(),
3530
            'p' => 'innerHTML',
3531
            'v' => $total,
3532
        );
3533
        $js[] = array(
3534
            't' => 'class',
3535
            's' => $ajaxObject->TotalItemsClassName(),
3536
            'p' => 'innerHTML',
3537
            'v' => $this->TotalItems($recalculate = true),
3538
        );
3539
        $js[] = array(
3540
            't' => 'class',
3541
            's' => $ajaxObject->TotalItemsTimesQuantityClassName(),
3542
            'p' => 'innerHTML',
3543
            'v' => $this->TotalItemsTimesQuantity(),
3544
        );
3545
        $js[] = array(
3546
            't' => 'class',
3547
            's' => $ajaxObject->ExpectedCountryClassName(),
3548
            'p' => 'innerHTML',
3549
            'v' => $this->ExpectedCountryName(),
3550
        );
3551
3552
        return $js;
3553
    }
3554
3555
    /**
3556
     * @ToDO: move to more appropriate class
3557
     *
3558
     * @return float
3559
     **/
3560
    public function SubTotalCartValue()
3561
    {
3562
        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...
3563
    }
3564
3565
/*******************************************************
3566
   * 10. STANDARD SS METHODS (requireDefaultRecords, onBeforeDelete, etc...)
3567
*******************************************************/
3568
3569
    /**
3570
     *standard SS method.
3571
     **/
3572
    public function populateDefaults()
3573
    {
3574
        parent::populateDefaults();
3575
    }
3576
3577
    public function onBeforeWrite()
3578
    {
3579
        parent::onBeforeWrite();
3580
        if (! $this->getCanHaveShippingAddress()) {
3581
            $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...
3582
        }
3583
        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...
3584
            $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...
3585
        }
3586
        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...
3587
            $generator = Injector::inst()->create('RandomGenerator');
3588
            $token = $generator->randomToken('sha1');
3589
            $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...
3590
        }
3591
    }
3592
3593
    /**
3594
     * standard SS method
3595
     * adds the ability to update order after writing it.
3596
     **/
3597
    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...
3598
    {
3599
        parent::onAfterWrite();
3600
        //crucial!
3601
        self::set_needs_recalculating(true, $this->ID);
3602
        // quick double-check
3603
        if ($this->IsCancelled() && ! $this->IsArchived()) {
3604
            $this->Archive($avoidWrites = true);
3605
        }
3606
        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...
3607
            //do nothing
3608
        } else {
3609
            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...
3610
                $this->calculateOrderAttributes($recalculate = false);
3611
                if (EcommerceRole::current_member_is_shop_admin()) {
3612
                    if (isset($_REQUEST['SubmitOrderViaCMS'])) {
3613
                        $this->tryToFinaliseOrder();
3614
                        //just in case it writes again...
3615
                        unset($_REQUEST['SubmitOrderViaCMS']);
3616
                    }
3617
                }
3618
            }
3619
        }
3620
    }
3621
3622
    /**
3623
     *standard SS method.
3624
     *
3625
     * delete attributes, statuslogs, and payments
3626
     * THIS SHOULD NOT BE USED AS ORDERS SHOULD BE CANCELLED NOT DELETED
3627
     */
3628
    public function onBeforeDelete()
3629
    {
3630
        parent::onBeforeDelete();
3631
        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...
3632
            foreach ($attributes as $attribute) {
3633
                $attribute->delete();
3634
                $attribute->destroy();
3635
            }
3636
        }
3637
3638
        //THE REST WAS GIVING ERRORS - POSSIBLY DUE TO THE FUNNY RELATIONSHIP (one-one, two times...)
3639
        /*
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...
3640
        if($billingAddress = $this->BillingAddress()) {
3641
            if($billingAddress->exists()) {
3642
                $billingAddress->delete();
3643
                $billingAddress->destroy();
3644
            }
3645
        }
3646
        if($shippingAddress = $this->ShippingAddress()) {
3647
            if($shippingAddress->exists()) {
3648
                $shippingAddress->delete();
3649
                $shippingAddress->destroy();
3650
            }
3651
        }
3652
3653
        if($statuslogs = $this->OrderStatusLogs()){
3654
            foreach($statuslogs as $log){
3655
                $log->delete();
3656
                $log->destroy();
3657
            }
3658
        }
3659
        if($payments = $this->Payments()){
3660
            foreach($payments as $payment){
3661
                $payment->delete();
3662
                $payment->destroy();
3663
            }
3664
        }
3665
        if($emails = $this->Emails()) {
3666
            foreach($emails as $email){
3667
                $email->delete();
3668
                $email->destroy();
3669
            }
3670
        }
3671
        */
3672
    }
3673
3674
/*******************************************************
3675
   * 11. DEBUG
3676
*******************************************************/
3677
3678
    /**
3679
     * Debug helper method.
3680
     * Can be called from /shoppingcart/debug/.
3681
     *
3682
     * @return string
3683
     */
3684
    public function debug()
3685
    {
3686
        $this->calculateOrderAttributes(true);
3687
3688
        return EcommerceTaskDebugCart::debug_object($this);
3689
    }
3690
}
3691