Completed
Push — master ( d3cc92...9135c9 )
by Nicolaas
03:31
created

Order::IsPaidNice()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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

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

namespace YourVendor;

class YourClass { }

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

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

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

Loading history...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
39
        'view' => array(
40
            'OrderEmail',
41
            'EmailLink',
42
            'PrintLink',
43
            'RetrieveLink',
44
            'ShareLink',
45
            '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
        'OrderProcessQueue' => 'OrderProcessQueue' //there is usually only one.
106
    );
107
108
    /**
109
     * standard SS variable.
110
     *
111
     * @var array
112
     */
113
    private static $indexes = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $indexes is not used and could be removed.

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

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

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

Loading history...
123
124
    /**
125
     * standard SS variable.
126
     *
127
     * @var array
128
     */
129
    private static $casting = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $casting is not used and could be removed.

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

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

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

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

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

Loading history...
173
    public function i18n_plural_name()
174
    {
175
        return _t('Order.ORDERS', 'Orders');
176
    }
177
178
    /**
179
     * Standard SS variable.
180
     *
181
     * @var string
182
     */
183
    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...
184
185
    /**
186
     * Tells us if an order needs to be recalculated
187
     * can save one for each order...
188
     *
189
     * @var array
190
     */
191
    private static $_needs_recalculating = array();
192
193
    /**
194
     * @param bool (optional) $b
195
     * @param int (optional)  $orderID
196
     *
197
     * @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...
198
     */
199
    public static function set_needs_recalculating($b = true, $orderID = 0)
200
    {
201
        self::$_needs_recalculating[$orderID] = $b;
202
    }
203
204
    /**
205
     * @param int (optional) $orderID
206
     *
207
     * @return bool
208
     */
209
    public static function get_needs_recalculating($orderID = 0)
210
    {
211
        return isset(self::$_needs_recalculating[$orderID]) ? self::$_needs_recalculating[$orderID] : false;
212
    }
213
214
    /**
215
     * Total Items : total items in cart
216
     * We start with -1 to easily identify if it has been run before.
217
     *
218
     * @var int
219
     */
220
    protected $totalItems = null;
221
222
    /**
223
     * Total Items : total items in cart
224
     * We start with -1 to easily identify if it has been run before.
225
     *
226
     * @var float
227
     */
228
    protected $totalItemsTimesQuantity = null;
229
230
    /**
231
     * Returns a set of modifier forms for use in the checkout order form,
232
     * Controller is optional, because the orderForm has its own default controller.
233
     *
234
     * This method only returns the Forms that should be included outside
235
     * the editable table... Forms within it can be called
236
     * from through the modifier itself.
237
     *
238
     * @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...
239
     * @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...
240
     *
241
     * @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...
242
     **/
243
    public function getModifierForms(Controller $optionalController = null, Validator $optionalValidator = null)
244
    {
245
        $arrayList = new ArrayList();
246
        $modifiers = $this->Modifiers();
247
        if ($modifiers->count()) {
248
            foreach ($modifiers as $modifier) {
249
                if ($modifier->ShowForm()) {
250
                    if ($form = $modifier->getModifierForm($optionalController, $optionalValidator)) {
251
                        $form->ShowFormInEditableOrderTable = $modifier->ShowFormInEditableOrderTable();
252
                        $form->ShowFormOutsideEditableOrderTable = $modifier->ShowFormOutsideEditableOrderTable();
253
                        $form->ModifierName = $modifier->ClassName;
254
                        $arrayList->push($form);
255
                    }
256
                }
257
            }
258
        }
259
        if ($arrayList->count()) {
260
            return $arrayList;
261
        } else {
262
            return;
263
        }
264
    }
265
266
    /**
267
     * This function returns the OrderSteps.
268
     *
269
     * @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...
270
     **/
271
    public static function get_order_status_options()
272
    {
273
        return OrderStep::get();
274
    }
275
276
    /**
277
     * Like the standard byID, but it checks whether we are allowed to view the order.
278
     *
279
     * @return: Order | Null
280
     **/
281
    public static function get_by_id_if_can_view($id)
282
    {
283
        $order = Order::get()->byID($id);
284
        if ($order && $order->canView()) {
285
            if ($order->IsSubmitted()) {
286
                // LITTLE HACK TO MAKE SURE WE SHOW THE LATEST INFORMATION!
287
                $order->tryToFinaliseOrder();
288
            }
289
290
            return $order;
291
        }
292
293
        return;
294
    }
295
296
    /**
297
     * returns a Datalist with the submitted order log included
298
     * this allows you to sort the orders by their submit dates.
299
     * You can retrieve this list and then add more to it (e.g. additional filters, additional joins, etc...).
300
     *
301
     * @param bool $onlySubmittedOrders - only include Orders that have already been submitted.
302
     * @param bool $includeCancelledOrders - only include Orders that have already been submitted.
303
     *
304
     * @return DataList (Orders)
305
     */
306
    public static function get_datalist_of_orders_with_submit_record($onlySubmittedOrders = true, $includeCancelledOrders = false)
307
    {
308
        if ($onlySubmittedOrders) {
309
            $submittedOrderStatusLogClassName = EcommerceConfig::get('OrderStatusLog', 'order_status_log_class_used_for_submitting_order');
310
            $list = Order::get()
311
                ->LeftJoin('OrderStatusLog', '"Order"."ID" = "OrderStatusLog"."OrderID"')
312
                ->LeftJoin($submittedOrderStatusLogClassName, '"OrderStatusLog"."ID" = "'.$submittedOrderStatusLogClassName.'"."ID"')
313
                ->Sort('OrderStatusLog.Created', 'ASC');
314
            $where = ' ("OrderStatusLog"."ClassName" = \''.$submittedOrderStatusLogClassName.'\') ';
315
        } else {
316
            $list = Order::get();
317
            $where = ' ("StatusID" > 0) ';
318
        }
319
        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...
320
            //do nothing...
321
        } else {
322
            $where .= ' AND ("CancelledByID" = 0 OR "CancelledByID" IS NULL)';
323
        }
324
        $list = $list->where($where);
325
326
        return $list;
327
    }
328
329
/*******************************************************
330
   * 1. CMS STUFF
331
*******************************************************/
332
333
    /**
334
     * fields that we remove from the parent::getCMSFields object set.
335
     *
336
     * @var array
337
     */
338
    protected $fieldsAndTabsToBeRemoved = array(
339
        'MemberID',
340
        'Attributes',
341
        'SessionID',
342
        'Emails',
343
        'BillingAddressID',
344
        'ShippingAddressID',
345
        'UseShippingAddress',
346
        'OrderStatusLogs',
347
        'Payments',
348
        'OrderDate',
349
        'ExchangeRate',
350
        'CurrencyUsedID',
351
        'StatusID',
352
        'Currency',
353
    );
354
355
    /**
356
     * STANDARD SILVERSTRIPE STUFF.
357
     **/
358
    private static $summary_fields = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $summary_fields is not used and could be removed.

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

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

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

Loading history...
374
        'ID' => array(
375
            'field' => 'NumericField',
376
            'title' => 'Order Number',
377
        ),
378
        'MemberID' => array(
379
            'field' => 'TextField',
380
            'filter' => 'OrderFilters_MemberAndAddress',
381
            'title' => 'Customer Details',
382
        ),
383
        'Created' => array(
384
            'field' => 'TextField',
385
            'filter' => 'OrderFilters_AroundDateFilter',
386
            'title' => 'Date (e.g. Today, 1 jan 2007, or last week)',
387
        ),
388
        //make sure to keep the items below, otherwise they do not show in form
389
        'StatusID' => array(
390
            'filter' => 'OrderFilters_MultiOptionsetStatusIDFilter',
391
        ),
392
        'CancelledByID' => array(
393
            'filter' => 'OrderFilters_HasBeenCancelled',
394
            'title' => 'Cancelled by ...',
395
        ),
396
    );
397
398
    /**
399
     * Determine which properties on the DataObject are
400
     * searchable, and map them to their default {@link FormField}
401
     * representations. Used for scaffolding a searchform for {@link ModelAdmin}.
402
     *
403
     * Some additional logic is included for switching field labels, based on
404
     * how generic or specific the field type is.
405
     *
406
     * Used by {@link SearchContext}.
407
     *
408
     * @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...
409
     *                       'fieldClasses': Associative array of field names as keys and FormField classes as values
410
     *                       'restrictFields': Numeric array of a field name whitelist
411
     *
412
     * @return FieldList
413
     */
414
    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...
415
    {
416
        $fieldList = parent::scaffoldSearchFields($_params);
417
418
        //for sales to action only show relevant ones ...
419
        if(Controller::curr() && Controller::curr()->class === 'SalesAdmin') {
420
            $statusOptions = OrderStep::admin_manageable_steps();
421
        } else {
422
            $statusOptions = OrderStep::get();
423
        }
424
        if ($statusOptions && $statusOptions->count()) {
425
            $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...
426
            $preSelected = array();
427
            $createdOrderStatus = $statusOptions->First();
428
            if ($createdOrderStatus) {
429
                $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...
430
            }
431
            $arrayOfStatusOptions = clone $statusOptions->map('ID', 'Title');
432
            $arrayOfStatusOptionsFinal = array();
433
            if (count($arrayOfStatusOptions)) {
434
                foreach ($arrayOfStatusOptions as $key => $value) {
435
                    if (isset($_GET['q']['StatusID'][$key])) {
436
                        $preSelected[$key] = $key;
437
                    }
438
                    $count = Order::get()
439
                        ->Filter(array('StatusID' => intval($key)))
440
                        ->count();
441
                    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...
442
                        //do nothing
443
                    } else {
444
                        $arrayOfStatusOptionsFinal[$key] = $value." ($count)";
445
                    }
446
                }
447
            }
448
            $statusField = new CheckboxSetField(
449
                'StatusID',
450
                Injector::inst()->get('OrderStep')->i18n_singular_name(),
451
                $arrayOfStatusOptionsFinal,
452
                $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...
453
            );
454
            $fieldList->push($statusField);
455
        }
456
        $fieldList->push(new DropdownField('CancelledByID', 'Cancelled', array(-1 => '(Any)', 1 => 'yes', 0 => 'no')));
457
458
        //allow changes
459
        $this->extend('scaffoldSearchFields', $fieldList, $_params);
460
461
        return $fieldList;
462
    }
463
464
    /**
465
     * link to edit the record.
466
     *
467
     * @param string | Null $action - e.g. edit
468
     *
469
     * @return string
470
     */
471
    public function CMSEditLink($action = null)
472
    {
473
        return Controller::join_links(
474
            Director::baseURL(),
475
            '/admin/sales-advanced/'.$this->ClassName.'/EditForm/field/'.$this->ClassName.'/item/'.$this->ID.'/',
476
            $action
477
        );
478
    }
479
480
    /**
481
     * STANDARD SILVERSTRIPE STUFF
482
     * broken up into submitted and not (yet) submitted.
483
     **/
484
    public function getCMSFields()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

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

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

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
485
    {
486
        $fields = $this->scaffoldFormFields(array(
487
            // Don't allow has_many/many_many relationship editing before the record is first saved
488
            'includeRelations' => false,
489
            'tabbed' => true,
490
            'ajaxSafe' => true
491
        ));
492
        $fields->insertBefore(
493
            Tab::create(
494
                'Next',
495
                _t('Order.NEXT_TAB', 'Action')
496
            ),
497
            '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...
498
        );
499
        $fields->addFieldsToTab(
500
            'Root',
501
            array(
502
                Tab::create(
503
                    "Items",
504
                    _t('Order.ITEMS_TAB', 'Items')
505
                ),
506
                Tab::create(
507
                    "Extras",
508
                    _t('Order.MODIFIERS_TAB', 'Adjustments')
509
                ),
510
                Tab::create(
511
                    'Emails',
512
                    _t('Order.EMAILS_TAB', 'Emails')
513
                ),
514
                Tab::create(
515
                    'Payments',
516
                    _t('Order.PAYMENTS_TAB', 'Payment')
517
                ),
518
                Tab::create(
519
                    'Account',
520
                    _t('Order.ACCOUNT_TAB', 'Account')
521
                ),
522
                Tab::create(
523
                    'Currency',
524
                    _t('Order.CURRENCY_TAB', 'Currency')
525
                ),
526
                Tab::create(
527
                    'Addresses',
528
                    _t('Order.ADDRESSES_TAB', 'Addresses')
529
                ),
530
                Tab::create(
531
                    'Log',
532
                    _t('Order.LOG_TAB', 'Notes')
533
                ),
534
                Tab::create(
535
                    'Cancellations',
536
                    _t('Order.CANCELLATION_TAB', 'Cancel')
537
                ),
538
            )
539
        );
540
        //as we are no longer using the parent:;getCMSFields
541
        // we had to add the updateCMSFields hook.
542
        $this->extend('updateCMSFields', $fields);
543
        $currentMember = Member::currentUser();
544
        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...
545
            $firstStep = OrderStep::get()->First();
546
            $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...
547
            $this->write();
548
        }
549
        $submitted = $this->IsSubmitted() ? true : false;
550
        if ($submitted) {
551
            //TODO
552
            //Having trouble here, as when you submit the form (for example, a payment confirmation)
553
            //as the step moves forward, meaning the fields generated are incorrect, causing an error
554
            //"I can't handle sub-URLs of a Form object." generated by the RequestHandler.
555
            //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
556
            //Or something similar.
557
            //why not check if the URL == $this->CMSEditLink()
558
            //and only tryToFinaliseOrder if this is true....
559
            if ($_SERVER['REQUEST_URI'] == $this->CMSEditLink() || $_SERVER['REQUEST_URI'] == $this->CMSEditLink('edit')) {
560
                $this->tryToFinaliseOrder();
561
            }
562
        } else {
563
            $this->init(true);
564
            $this->calculateOrderAttributes(true);
565
            Session::set('EcommerceOrderGETCMSHack', $this->ID);
566
        }
567
        if ($submitted) {
568
            $this->fieldsAndTabsToBeRemoved[] = 'CustomerOrderNote';
569
        } else {
570
            $this->fieldsAndTabsToBeRemoved[] = 'Emails';
571
        }
572
        foreach ($this->fieldsAndTabsToBeRemoved as $field) {
573
            $fields->removeByName($field);
574
        }
575
        $orderSummaryConfig = GridFieldConfig_Base::create();
576
        $orderSummaryConfig->removeComponentsByType('GridFieldToolbarHeader');
577
        // $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...
578
        $orderSummaryConfig->removeComponentsByType('GridFieldFilterHeader');
579
        $orderSummaryConfig->removeComponentsByType('GridFieldPageCount');
580
        $orderSummaryConfig->removeComponentsByType('GridFieldPaginator');
581
        $nextFieldArray = array(
582
            LiteralField::create('CssFix', '<style>#Root_Next h2.form-control {padding: 0!important; margin: 0!important; padding-top: 4em!important;}</style>'),
583
            HeaderField::create('MyOrderStepHeader', _t('Order.CURRENT_STATUS', '1. Current Status')),
584
            $this->OrderStepField(),
585
            GridField::create(
586
                'OrderSummary',
587
                _t('Order.CURRENT_STATUS', 'Summary'),
588
                ArrayList::create(array($this)),
589
                $orderSummaryConfig
590
            )
591
        );
592
        $keyNotes = OrderStatusLog::get()->filter(
593
            array(
594
                'OrderID' => $this->ID,
595
                'ClassName' => 'OrderStatusLog'
596
            )
597
        );
598
        if ($keyNotes->count()) {
599
            $notesSummaryConfig = GridFieldConfig_RecordViewer::create();
600
            $notesSummaryConfig->removeComponentsByType('GridFieldToolbarHeader');
601
            $notesSummaryConfig->removeComponentsByType('GridFieldFilterHeader');
602
            // $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...
603
            $notesSummaryConfig->removeComponentsByType('GridFieldPageCount');
604
            $notesSummaryConfig->removeComponentsByType('GridFieldPaginator');
605
            $nextFieldArray = array_merge(
606
                $nextFieldArray,
607
                array(
608
                    HeaderField::create('KeyNotesHeader', _t('Order.KEY_NOTES_HEADER', 'Key Notes')),
609
                    GridField::create(
610
                        'OrderStatusLogSummary',
611
                        _t('Order.CURRENT_KEY_NOTES', 'Key Notes'),
612
                        $keyNotes,
613
                        $notesSummaryConfig
614
                    )
615
                )
616
            );
617
        }
618
        $nextFieldArray = array_merge(
619
            $nextFieldArray,
620
            array(
621
                EcommerceCMSButtonField::create(
622
                    'AddNoteButton',
623
                    $this->CMSEditLink('ItemEditForm/field/OrderStatusLog/item/new'),
624
                    _t('Order.ADD_NOTE', 'Add Note')
625
                )
626
            )
627
        );
628
        $nextFieldArray = array_merge(
629
            $nextFieldArray,
630
            array(
631
632
            )
633
        );
634
635
         //is the member is a shop admin they can always view it
636
637
        if (EcommerceRole::current_member_can_process_orders(Member::currentUser())) {
638
            $lastStep = OrderStep::get()->Last();
639
            if($this->StatusID != $lastStep->ID) {
0 ignored issues
show
Documentation introduced by
The property StatusID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

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

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

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

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

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

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

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

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

class ParentClass {
    private $data = array();

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

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

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

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

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

Loading history...
751
            if ($member && $member->exists()) {
752
                $fields->addFieldToTab('Root.Account', new LiteralField('MemberDetails', $member->getEcommerceFieldsForCMS()));
753
            } else {
754
                $fields->addFieldToTab('Root.Account', new LiteralField('MemberDetails',
755
                    '<p>'._t('Order.NO_ACCOUNT', 'There is no --- account --- associated with this order').'</p>'
756
                ));
757
            }
758
            $cancelledField = $fields->dataFieldByName('CancelledByID');
759
            $fields->removeByName('CancelledByID');
760
            $shopAdminAndCurrentCustomerArray = EcommerceRole::list_of_admins(true);
761
            if ($member && $member->exists()) {
762
                $shopAdminAndCurrentCustomerArray[$member->ID] = $member->getName();
763
            }
764
            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...
765
                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...
766
                    $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...
767
                }
768
            }
769
            if ($this->canCancel()) {
770
                $fields->addFieldsToTab(
771
                    'Root.Cancellations',
772
                    array(
773
                        DropdownField::create(
774
                            'CancelledByID',
775
                            $cancelledField->Title(),
776
                            $shopAdminAndCurrentCustomerArray
777
                        )
778
                    )
779
                );
780
            } else {
781
                $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...
782
                $fields->addFieldsToTab(
783
                    'Root.Cancellations',
784
                    ReadonlyField::create(
785
                        'CancelledByDisplay',
786
                        $cancelledField->Title(),
787
                        $cancelledBy
788
789
                    )
790
                );
791
            }
792
            $fields->addFieldToTab('Root.Log', $this->getOrderStatusLogsTableField_Archived());
793
            $submissionLog = $this->SubmissionLog();
794
            if ($submissionLog) {
795
                $fields->addFieldToTab('Root.Log',
796
                    ReadonlyField::create(
797
                        'SequentialOrderNumber',
798
                        _t('Order.SEQUENTIALORDERNUMBER', 'Consecutive order number'),
799
                        $submissionLog->SequentialOrderNumber
800
                    )->setRightTitle('e.g. 1,2,3,4,5...')
801
                );
802
            }
803
        } else {
804
            $linkText = _t(
805
                'Order.LOAD_THIS_ORDER',
806
                'load this order'
807
            );
808
            $message = _t(
809
                'Order.NOSUBMITTEDYET',
810
                '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 .',
811
                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...
812
            );
813
            $fields->addFieldToTab('Root.Next', new LiteralField('MainDetails', '<p>'.$message.'</p>'));
814
            $fields->addFieldToTab('Root.Items', $this->getOrderItemsField());
815
            $fields->addFieldToTab('Root.Extras', $this->getModifierTableField());
816
817
            //MEMBER STUFF
818
            $specialOptionsArray = array();
819
            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...
820
                $specialOptionsArray[0] = _t('Order.SELECTCUSTOMER', '--- Remover Customer ---');
821
                $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...
822
            } elseif ($currentMember) {
823
                $specialOptionsArray[0] = _t('Order.SELECTCUSTOMER', '--- Select Customers ---');
824
                $currentMemberID = $currentMember->ID;
825
                $specialOptionsArray[$currentMemberID] = _t('Order.ASSIGNTHISORDERTOME', '- Assign this order to me: ').$currentMember->getTitle();
826
            }
827
            //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...
828
            $memberArray = $specialOptionsArray + EcommerceRole::list_of_customers(true);
829
            $fields->addFieldToTab('Root.Next', new DropdownField('MemberID', _t('Order.SELECTCUSTOMER', 'Select Customer'), $memberArray), 'CustomerOrderNote');
830
            $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...
831
        }
832
        $fields->addFieldToTab('Root.Addresses', new HeaderField('BillingAddressHeader', _t('Order.BILLINGADDRESS', 'Billing Address')));
833
834
        $fields->addFieldToTab('Root.Addresses', $this->getBillingAddressField());
835
836
        if (EcommerceConfig::get('OrderAddress', 'use_separate_shipping_address')) {
837
            $fields->addFieldToTab('Root.Addresses', new HeaderField('ShippingAddressHeader', _t('Order.SHIPPINGADDRESS', 'Shipping Address')));
838
            $fields->addFieldToTab('Root.Addresses', new CheckboxField('UseShippingAddress', _t('Order.USESEPERATEADDRESS', 'Use separate shipping address?')));
839
            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...
840
                $fields->addFieldToTab('Root.Addresses', $this->getShippingAddressField());
841
            }
842
        }
843
        $currencies = EcommerceCurrency::get_list();
844
        if ($currencies && $currencies->count()) {
845
            $currencies = $currencies->map()->toArray();
846
            $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...
847
            $fields->addFieldToTab('Root.Currency', $currencyField = new DropdownField('CurrencyUsedID', _t('Order.CurrencyUsed', 'Currency Used'), $currencies));
848
            if ($this->IsSubmitted()) {
849
                $fields->replaceField('CurrencyUsedID', $fields->dataFieldByName('CurrencyUsedID')->performReadonlyTransformation());
850
            }
851
        } else {
852
            $fields->addFieldToTab('Root.Currency', new LiteralField('CurrencyInfo', '<p>You can not change currencies, because no currencies have been created.</p>'));
853
            $fields->replaceField('CurrencyUsedID', $fields->dataFieldByName('CurrencyUsedID')->performReadonlyTransformation());
854
        }
855
        $fields->addFieldToTab('Root.Log', new ReadonlyField('Created', _t('Root.CREATED', 'Created')));
856
        $fields->addFieldToTab('Root.Log', new ReadonlyField('LastEdited', _t('Root.LASTEDITED', 'Last saved')));
857
        $this->extend('updateCMSFields', $fields);
858
859
        return $fields;
860
    }
861
862
    /**
863
     * Field to add and edit Order Items.
864
     *
865
     * @return GridField
866
     */
867
    protected function getOrderItemsField()
868
    {
869
        $gridFieldConfig = GridFieldConfigForOrderItems::create();
870
        $source = $this->OrderItems();
871
872
        return new GridField('OrderItems', _t('OrderItems.PLURALNAME', 'Order Items'), $source, $gridFieldConfig);
873
    }
874
875
    /**
876
     * Field to add and edit Modifiers.
877
     *
878
     * @return GridField
879
     */
880
    public function getModifierTableField()
881
    {
882
        $gridFieldConfig = GridFieldConfigForOrderItems::create();
883
        $source = $this->Modifiers();
884
885
        return new GridField('OrderModifiers', _t('OrderItems.PLURALNAME', 'Order Items'), $source, $gridFieldConfig);
886
    }
887
888
    /**
889
     *@return GridField
890
     **/
891
    protected function getBillingAddressField()
892
    {
893
        $this->CreateOrReturnExistingAddress('BillingAddress');
894
        $gridFieldConfig = GridFieldConfig::create()->addComponents(
895
            new GridFieldToolbarHeader(),
896
            new GridFieldSortableHeader(),
897
            new GridFieldDataColumns(),
898
            new GridFieldPaginator(10),
899
            new GridFieldEditButton(),
900
            new GridFieldDetailForm()
901
        );
902
        //$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...
903
        $source = BillingAddress::get()->filter(array('OrderID' => $this->ID));
904
905
        return new GridField('BillingAddress', _t('BillingAddress.SINGULARNAME', 'Billing Address'), $source, $gridFieldConfig);
906
    }
907
908
    /**
909
     *@return GridField
910
     **/
911
    protected function getShippingAddressField()
912
    {
913
        $this->CreateOrReturnExistingAddress('ShippingAddress');
914
        $gridFieldConfig = GridFieldConfig::create()->addComponents(
915
            new GridFieldToolbarHeader(),
916
            new GridFieldSortableHeader(),
917
            new GridFieldDataColumns(),
918
            new GridFieldPaginator(10),
919
            new GridFieldEditButton(),
920
            new GridFieldDetailForm()
921
        );
922
        //$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...
923
        $source = ShippingAddress::get()->filter(array('OrderID' => $this->ID));
924
925
        return new GridField('ShippingAddress', _t('BillingAddress.SINGULARNAME', 'Shipping Address'), $source, $gridFieldConfig);
926
    }
927
928
    /**
929
     * Needs to be public because the OrderStep::getCMSFIelds accesses it.
930
     *
931
     * @param string    $sourceClass
932
     * @param string    $title
933
     *
934
     * @return GridField
935
     **/
936
    public function getOrderStatusLogsTableField(
937
        $sourceClass = 'OrderStatusLog',
938
        $title = ''
939
    ) {
940
        $gridFieldConfig = GridFieldConfig_RecordViewer::create()->addComponents(
941
            new GridFieldAddNewButton('toolbar-header-right'),
942
            new GridFieldDetailForm()
943
        );
944
        $title ? $title : $title = _t('OrderStatusLog.PLURALNAME', 'Order Status Logs');
945
        $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...
946
        $gf = new GridField($sourceClass, $title, $source, $gridFieldConfig);
947
        $gf->setModelClass($sourceClass);
948
949
        return $gf;
950
    }
951
952
    /**
953
     * Needs to be public because the OrderStep::getCMSFIelds accesses it.
954
     *
955
     * @param string    $sourceClass
956
     * @param string    $title
957
     *
958
     * @return GridField
959
     **/
960
    public function getOrderStatusLogsTableFieldEditable(
961
        $sourceClass = 'OrderStatusLog',
962
        $title = ''
963
    ) {
964
        $gf = $this->getOrderStatusLogsTableField($sourceClass, $title);
965
        $gf->getConfig()->addComponents(
966
            new GridFieldEditButton()
967
        );
968
        return $gf;
969
    }
970
971
    /**
972
     * @param string    $sourceClass
973
     * @param string    $title
974
     * @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...
975
     * @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...
976
     *
977
     * @return GridField
978
     **/
979
    protected function getOrderStatusLogsTableField_Archived(
980
        $sourceClass = 'OrderStatusLog',
981
        $title = '',
982
        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...
983
        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...
984
    ) {
985
        $title ? $title : $title = _t('OrderLog.PLURALNAME', 'Order Log');
986
        $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...
987
        if ($sourceClass != 'OrderStatusLog' && class_exists($sourceClass)) {
988
            $source = $source->filter(array('ClassName' => ClassInfo::subclassesFor($sourceClass)));
989
        }
990
        $gridField = GridField::create($sourceClass, $title, $source, $config = GridFieldConfig_RelationEditor::create());
991
        $config->removeComponentsByType('GridFieldAddExistingAutocompleter');
992
        $config->removeComponentsByType('GridFieldDeleteAction');
993
994
        return $gridField;
995
    }
996
997
    /**
998
     * @return GridField
999
     **/
1000
    public function getEmailsTableField()
1001
    {
1002
        $gridFieldConfig = GridFieldConfig_RecordViewer::create()->addComponents(
1003
            new GridFieldDetailForm()
1004
        );
1005
1006
        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...
1007
    }
1008
1009
    /**
1010
     * @return GridField
1011
     */
1012
    protected function getPaymentsField()
1013
    {
1014
        $gridFieldConfig = GridFieldConfig_RecordViewer::create()->addComponents(
1015
            new GridFieldDetailForm(),
1016
            new GridFieldEditButton()
1017
        );
1018
1019
        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...
1020
    }
1021
1022
    /**
1023
     * @return OrderStepField
1024
     */
1025
    public function OrderStepField()
1026
    {
1027
        return OrderStepField::create($name = 'MyOrderStep', $this, Member::currentUser());
1028
    }
1029
1030
/*******************************************************
1031
   * 2. MAIN TRANSITION FUNCTIONS
1032
*******************************************************/
1033
1034
    /**
1035
     * init runs on start of a new Order (@see onAfterWrite)
1036
     * it adds all the modifiers to the orders and the starting OrderStep.
1037
     *
1038
     * @param bool $recalculate
1039
     *
1040
     * @return DataObject (Order)
1041
     **/
1042
    public function init($recalculate = false)
1043
    {
1044
        if ($this->IsSubmitted()) {
1045
            user_error('Can not init an order that has been submitted', E_USER_NOTICE);
1046
        } else {
1047
            //to do: check if shop is open....
1048
            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...
1049
                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...
1050
                    $createdOrderStatus = OrderStep::get()->First();
1051
                    if (!$createdOrderStatus) {
1052
                        user_error('No ordersteps have been created', E_USER_WARNING);
1053
                    }
1054
                    $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...
1055
                }
1056
                $createdModifiersClassNames = array();
1057
                $modifiersAsArrayList = new ArrayList();
1058
                $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...
1059
                if ($modifiers->count()) {
1060
                    foreach ($modifiers as $modifier) {
1061
                        $modifiersAsArrayList->push($modifier);
1062
                    }
1063
                }
1064
                if ($modifiersAsArrayList->count()) {
1065
                    foreach ($modifiersAsArrayList as $modifier) {
1066
                        $createdModifiersClassNames[$modifier->ID] = $modifier->ClassName;
1067
                    }
1068
                } 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...
1069
                }
1070
                $modifiersToAdd = EcommerceConfig::get('Order', 'modifiers');
1071
                if (is_array($modifiersToAdd) && count($modifiersToAdd) > 0) {
1072
                    foreach ($modifiersToAdd as $numericKey => $className) {
1073
                        if (!in_array($className, $createdModifiersClassNames)) {
1074
                            if (class_exists($className)) {
1075
                                $modifier = new $className();
1076
                                //only add the ones that should be added automatically
1077
                                if (!$modifier->DoNotAddAutomatically()) {
1078
                                    if (is_a($modifier, 'OrderModifier')) {
1079
                                        $modifier->OrderID = $this->ID;
1080
                                        $modifier->Sort = $numericKey;
1081
                                        //init method includes a WRITE
1082
                                        $modifier->init();
1083
                                        //IMPORTANT - add as has_many relationship  (Attributes can be a modifier OR an OrderItem)
1084
                                        $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...
1085
                                        $modifiersAsArrayList->push($modifier);
1086
                                    }
1087
                                }
1088
                            } else {
1089
                                user_error('reference to a non-existing class: '.$className.' in modifiers', E_USER_NOTICE);
1090
                            }
1091
                        }
1092
                    }
1093
                }
1094
                $this->extend('onInit', $this);
1095
                //careful - this will call "onAfterWrite" again
1096
                $this->write();
1097
            }
1098
        }
1099
1100
        return $this;
1101
    }
1102
1103
    /**
1104
     * @var array
1105
     */
1106
    private static $_try_to_finalise_order_is_running = array();
1107
1108
    /**
1109
     * Goes through the order steps and tries to "apply" the next status to the order.
1110
     *
1111
     * @param bool $runAgain
1112
     * @param bool $fromOrderQueue - is it being called from the OrderProcessQueue (or similar)
1113
     **/
1114
    public function tryToFinaliseOrder($runAgain = false, $fromOrderQueue = false)
1115
    {
1116
        if (empty(self::$_try_to_finalise_order_is_running[$this->ID]) || $runAgain) {
1117
            self::$_try_to_finalise_order_is_running[$this->ID] = true;
1118
1119
            //if the order has been cancelled then we do not process it ...
1120
            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...
1121
                $this->Archive(true);
1122
1123
                return;
1124
            }
1125
            // if it is in the queue it has to run from the queue tasks
1126
            // if it ruins from the queue tasks then it has to be one currently processing.
1127
            $queueObjectSingleton = Injector::inst()->get('OrderProcessQueue');
1128
            if ($myQueueObject = $queueObjectSingleton->getQueueObject($this)) {
1129
                if($fromOrderQueue) {
1130
                    if ( ! $myQueueObject->InProcess) {
1131
                        return;
1132
                    }
1133
                } else {
1134
                    return;
1135
                }
1136
            }
1137
            //a little hack to make sure we do not rely on a stored value
1138
            //of "isSubmitted"
1139
            $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...
1140
            //status of order is being progressed
1141
            $nextStatusID = $this->doNextStatus();
1142
            if ($nextStatusID) {
1143
                $nextStatusObject = OrderStep::get()->byID($nextStatusID);
1144
                if ($nextStatusObject) {
1145
                    $delay = $nextStatusObject->CalculatedDeferTimeInSeconds($this);
1146
                    if ($delay > 0) {
1147
                        if ($nextStatusObject->DeferFromSubmitTime) {
1148
                            $delay = $delay - $this->SecondsSinceBeingSubmitted();
1149
                            if ($delay < 0) {
1150
                                $delay = 0;
1151
                            }
1152
                        }
1153
                        $queueObjectSingleton->AddOrderToQueue(
1154
                            $this,
1155
                            $delay
1156
                        );
1157
                    } else {
1158
                        //status has been completed, so it can be released
1159
                        self::$_try_to_finalise_order_is_running[$this->ID] = false;
1160
                        $this->tryToFinaliseOrder($runAgain, $fromOrderQueue);
1161
                    }
1162
                }
1163
            }
1164
        }
1165
    }
1166
1167
    /**
1168
     * Goes through the order steps and tries to "apply" the next step
1169
     * Step is updated after the other one is completed...
1170
     *
1171
     * @return int (StatusID or false if the next status can not be "applied")
1172
     **/
1173
    public function doNextStatus()
1174
    {
1175
        if ($this->MyStep()->initStep($this)) {
1176
            if ($this->MyStep()->doStep($this)) {
1177
                if ($nextOrderStepObject = $this->MyStep()->nextStep($this)) {
1178
                    $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...
1179
                    $this->write();
1180
1181
                    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...
1182
                }
1183
            }
1184
        }
1185
1186
        return 0;
1187
    }
1188
1189
    /**
1190
     * cancel an order.
1191
     *
1192
     * @param Member $member - (optional) the user cancelling the order
0 ignored issues
show
Documentation introduced by
Should the type for parameter $member not be Member|null?

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

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

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

Loading history...
1193
     * @param string $reason - (optional) the reason the order is cancelled
1194
     *
1195
     * @return OrderStatusLog_Cancel
1196
     */
1197
    public function Cancel($member = null, $reason = '')
1198
    {
1199
        if($member && $member instanceof Member) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

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

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

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

could be turned into

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

This is much more concise to read.

Loading history...
1200
            //we have a valid member
1201
        } else {
1202
            $member = EcommerceRole::get_default_shop_admin_user();
1203
        }
1204
        if($member) {
1205
            //archive and write
1206
            $this->Archive($avoidWrites = true);
1207
            if($avoidWrites) {
1208
                DB::query('Update "Order" SET CancelledByID = '.$member->ID.' WHERE ID = '.$this->ID.' LIMIT 1;');
1209
            } else {
1210
                $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...
1211
                $this->write();
1212
            }
1213
            //create log ...
1214
            $log = OrderStatusLog_Cancel::create();
1215
            $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...
1216
            $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...
1217
            $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...
1218
            if ($member->IsShopAdmin()) {
1219
                $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...
1220
            }
1221
            $log->write();
1222
            $this->extend('doCancel', $member, $log);
1223
1224
            return $log;
1225
        }
1226
    }
1227
1228
    /**
1229
     * returns true if successful.
1230
     *
1231
     * @param bool $avoidWrites
1232
     *
1233
     * @return bool
1234
     */
1235
    public function Archive($avoidWrites = true)
1236
    {
1237
        $lastOrderStep = OrderStep::get()->Last();
1238
        if ($lastOrderStep) {
1239
            if ($avoidWrites) {
1240
                DB::query('
1241
                    UPDATE "Order"
1242
                    SET "Order"."StatusID" = '.$lastOrderStep->ID.'
1243
                    WHERE "Order"."ID" = '.$this->ID.'
1244
                    LIMIT 1
1245
                ');
1246
1247
                return true;
1248
            } else {
1249
                $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...
1250
                $this->write();
1251
1252
                return true;
1253
            }
1254
        }
1255
1256
        return false;
1257
    }
1258
1259
/*******************************************************
1260
   * 3. STATUS RELATED FUNCTIONS / SHORTCUTS
1261
*******************************************************/
1262
1263
    /**
1264
     * Avoids caching of $this->Status().
1265
     *
1266
     * @return DataObject (current OrderStep)
1267
     */
1268
    public function MyStep()
1269
    {
1270
        $step = null;
1271
        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...
1272
            $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...
1273
        }
1274
        if (!$step) {
1275
            $step = OrderStep::get()->First(); //TODO: this could produce strange results
1276
        }
1277
        if (!$step) {
1278
            $step = OrderStep_Created::create();
1279
        }
1280
        if (!$step) {
1281
            user_error('You need an order step in your Database.');
1282
        }
1283
1284
        return $step;
1285
    }
1286
1287
    /**
1288
     * Return the OrderStatusLog that is relevant to the Order status.
1289
     *
1290
     * @return OrderStatusLog
1291
     */
1292
    public function RelevantLogEntry()
1293
    {
1294
        return $this->MyStep()->RelevantLogEntry($this);
1295
    }
1296
1297
    /**
1298
     * @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...
1299
     */
1300
    public function CurrentStepVisibleToCustomer()
1301
    {
1302
        $obj = $this->MyStep();
1303
        if ($obj->HideStepFromCustomer) {
1304
            $obj = OrderStep::get()->where('"OrderStep"."Sort" < '.$obj->Sort.' AND "HideStepFromCustomer" = 0')->Last();
1305
            if (!$obj) {
1306
                $obj = OrderStep::get()->First();
1307
            }
1308
        }
1309
1310
        return $obj;
1311
    }
1312
1313
    /**
1314
     * works out if the order is still at the first OrderStep.
1315
     *
1316
     * @return bool
1317
     */
1318
    public function IsFirstStep()
1319
    {
1320
        $firstStep = OrderStep::get()->First();
1321
        $currentStep = $this->MyStep();
1322
        if ($firstStep && $currentStep) {
1323
            if ($firstStep->ID == $currentStep->ID) {
1324
                return true;
1325
            }
1326
        }
1327
1328
        return false;
1329
    }
1330
1331
    /**
1332
     * Is the order still being "edited" by the customer?
1333
     *
1334
     * @return bool
1335
     */
1336
    public function IsInCart()
1337
    {
1338
        return (bool) $this->IsSubmitted() ? false : true;
1339
    }
1340
1341
    /**
1342
     * The order has "passed" the IsInCart phase.
1343
     *
1344
     * @return bool
1345
     */
1346
    public function IsPastCart()
1347
    {
1348
        return (bool) $this->IsInCart() ? false : true;
1349
    }
1350
1351
    /**
1352
     * Are there still steps the order needs to go through?
1353
     *
1354
     * @return bool
1355
     */
1356
    public function IsUncomplete()
1357
    {
1358
        return (bool) $this->MyStep()->ShowAsUncompletedOrder;
1359
    }
1360
1361
    /**
1362
     * Is the order in the :"processing" phaase.?
1363
     *
1364
     * @return bool
1365
     */
1366
    public function IsProcessing()
1367
    {
1368
        return (bool) $this->MyStep()->ShowAsInProcessOrder;
1369
    }
1370
1371
    /**
1372
     * Is the order completed?
1373
     *
1374
     * @return bool
1375
     */
1376
    public function IsCompleted()
1377
    {
1378
        return (bool) $this->MyStep()->ShowAsCompletedOrder;
1379
    }
1380
1381
    /**
1382
     * Has the order been paid?
1383
     * TODO: why do we check if there is a total at all?
1384
     *
1385
     * @return bool
1386
     */
1387
    public function IsPaid()
1388
    {
1389
        if ($this->IsSubmitted()) {
1390
            return (bool) (($this->Total() >= 0) && ($this->TotalOutstanding() <= 0));
1391
        }
1392
1393
        return false;
1394
    }
1395
    /**
1396
     * Has the order been paid?
1397
     * TODO: why do we check if there is a total at all?
1398
     *
1399
     * @return Boolean (object)
0 ignored issues
show
Documentation introduced by
Should the return type not be DBField?

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

Loading history...
1400
     */
1401
    public function IsPaidNice()
1402
    {
1403
        return  DBField::create_field('Boolean', $this->IsPaid());
1404
    }
1405
1406
    /**
1407
     * Has the order been paid?
1408
     * TODO: why do we check if there is a total at all?
1409
     *
1410
     * @return bool
1411
     */
1412
    public function PaymentIsPending()
1413
    {
1414
        if ($this->IsSubmitted()) {
1415
            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...
1416
                //do nothing;
1417
            } 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...
1418
                foreach ($payments as $payment) {
1419
                    if ('Pending' == $payment->Status) {
1420
                        return true;
1421
                    }
1422
                }
1423
            }
1424
        }
1425
1426
        return false;
1427
    }
1428
1429
    /**
1430
     * shows payments that are meaningfull
1431
     * if the order has been paid then only show successful payments.
1432
     *
1433
     * @return DataList
1434
     */
1435
    public function RelevantPayments()
1436
    {
1437
        if ($this->IsPaid()) {
1438
            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...
1439
            //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...
1440
            //	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...
1441
        } else {
1442
            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...
1443
        }
1444
    }
1445
1446
    /**
1447
     * Has the order been cancelled?
1448
     *
1449
     * @return bool
1450
     */
1451
    public function IsCancelled()
1452
    {
1453
        return $this->getIsCancelled();
1454
    }
1455
    public function getIsCancelled()
1456
    {
1457
        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...
1458
    }
1459
1460
    /**
1461
     * Has the order been cancelled by the customer?
1462
     *
1463
     * @return bool
1464
     */
1465
    public function IsCustomerCancelled()
1466
    {
1467
        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...
1468
            return true;
1469
        }
1470
1471
        return false;
1472
    }
1473
1474
    /**
1475
     * Has the order been cancelled by the  administrator?
1476
     *
1477
     * @return bool
1478
     */
1479
    public function IsAdminCancelled()
1480
    {
1481
        if ($this->IsCancelled()) {
1482
            if (!$this->IsCustomerCancelled()) {
1483
                $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...
1484
                if ($admin) {
1485
                    if ($admin->IsShopAdmin()) {
1486
                        return true;
1487
                    }
1488
                }
1489
            }
1490
        }
1491
1492
        return false;
1493
    }
1494
1495
    /**
1496
     * Is the Shop Closed for business?
1497
     *
1498
     * @return bool
1499
     */
1500
    public function ShopClosed()
1501
    {
1502
        return EcomConfig()->ShopClosed;
1503
    }
1504
1505
/*******************************************************
1506
   * 4. LINKING ORDER WITH MEMBER AND ADDRESS
1507
*******************************************************/
1508
1509
    /**
1510
     * Returns a member linked to the order.
1511
     * If a member is already linked, it will return the existing member.
1512
     * Otherwise it will return a new Member.
1513
     *
1514
     * Any new member is NOT written, because we dont want to create a new member unless we have to!
1515
     * We will not add a member to the order unless a new one is created in the checkout
1516
     * OR the member is logged in / logs in.
1517
     *
1518
     * Also note that if a new member is created, it is not automatically written
1519
     *
1520
     * @param bool $forceCreation - if set to true then the member will always be saved in the database.
1521
     *
1522
     * @return Member
1523
     **/
1524
    public function CreateOrReturnExistingMember($forceCreation = false)
1525
    {
1526
        if ($this->IsSubmitted()) {
1527
            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...
1528
        }
1529
        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...
1530
            $member = $this->Member();
0 ignored issues
show
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...
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...
1531
        } elseif ($member = Member::currentUser()) {
1532
            if (!$member->IsShopAdmin()) {
1533
                $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...
1534
                $this->write();
1535
            }
1536
        }
1537
        $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...
1538
        if (!$member) {
1539
            $member = new Member();
1540
        }
1541
        if ($member && $forceCreation) {
1542
            $member->write();
1543
        }
1544
1545
        return $member;
1546
    }
1547
1548
    /**
1549
     * Returns either the existing one or a new Order Address...
1550
     * All Orders will have a Shipping and Billing address attached to it.
1551
     * Method used to retrieve object e.g. for $order->BillingAddress(); "BillingAddress" is the method name you can use.
1552
     * If the method name is the same as the class name then dont worry about providing one.
1553
     *
1554
     * @param string $className             - ClassName of the Address (e.g. BillingAddress or ShippingAddress)
1555
     * @param string $alternativeMethodName - method to retrieve Address
1556
     **/
1557
    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...
1558
    {
1559
        if ($this->exists()) {
1560
            $methodName = $className;
1561
            if ($alternativeMethodName) {
1562
                $methodName = $alternativeMethodName;
1563
            }
1564
            if ($this->IsSubmitted()) {
1565
                return $this->$methodName();
1566
            }
1567
            $variableName = $className.'ID';
1568
            $address = null;
1569
            if ($this->$variableName) {
1570
                $address = $this->$methodName();
1571
            }
1572
            if (!$address) {
1573
                $address = new $className();
1574
                if ($member = $this->CreateOrReturnExistingMember()) {
1575
                    if ($member->exists()) {
1576
                        $address->FillWithLastAddressFromMember($member, $write = false);
1577
                    }
1578
                }
1579
            }
1580
            if ($address) {
1581
                if (!$address->exists()) {
1582
                    $address->write();
1583
                }
1584
                if ($address->OrderID != $this->ID) {
1585
                    $address->OrderID = $this->ID;
1586
                    $address->write();
1587
                }
1588
                if ($this->$variableName != $address->ID) {
1589
                    if (!$this->IsSubmitted()) {
1590
                        $this->$variableName = $address->ID;
1591
                        $this->write();
1592
                    }
1593
                }
1594
1595
                return $address;
1596
            }
1597
        }
1598
1599
        return;
1600
    }
1601
1602
    /**
1603
     * Sets the country in the billing and shipping address.
1604
     *
1605
     * @param string $countryCode            - code for the country e.g. NZ
1606
     * @param bool   $includeBillingAddress
1607
     * @param bool   $includeShippingAddress
1608
     **/
1609
    public function SetCountryFields($countryCode, $includeBillingAddress = true, $includeShippingAddress = true)
1610
    {
1611
        if ($this->IsSubmitted()) {
1612
            user_error('Can not change country in submitted order', E_USER_NOTICE);
1613
        } else {
1614
            if ($includeBillingAddress) {
1615
                if ($billingAddress = $this->CreateOrReturnExistingAddress('BillingAddress')) {
1616
                    $billingAddress->SetCountryFields($countryCode);
1617
                }
1618
            }
1619
            if (EcommerceConfig::get('OrderAddress', 'use_separate_shipping_address')) {
1620
                if ($includeShippingAddress) {
1621
                    if ($shippingAddress = $this->CreateOrReturnExistingAddress('ShippingAddress')) {
1622
                        $shippingAddress->SetCountryFields($countryCode);
1623
                    }
1624
                }
1625
            }
1626
        }
1627
    }
1628
1629
    /**
1630
     * Sets the region in the billing and shipping address.
1631
     *
1632
     * @param int $regionID - ID for the region to be set
1633
     **/
1634
    public function SetRegionFields($regionID)
1635
    {
1636
        if ($this->IsSubmitted()) {
1637
            user_error('Can not change country in submitted order', E_USER_NOTICE);
1638
        } else {
1639
            if ($billingAddress = $this->CreateOrReturnExistingAddress('BillingAddress')) {
1640
                $billingAddress->SetRegionFields($regionID);
1641
            }
1642
            if ($this->CanHaveShippingAddress()) {
1643
                if ($shippingAddress = $this->CreateOrReturnExistingAddress('ShippingAddress')) {
1644
                    $shippingAddress->SetRegionFields($regionID);
1645
                }
1646
            }
1647
        }
1648
    }
1649
1650
    /**
1651
     * Stores the preferred currency of the order.
1652
     * IMPORTANTLY we store the exchange rate for future reference...
1653
     *
1654
     * @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...
1655
     */
1656
    public function UpdateCurrency($newCurrency)
1657
    {
1658
        if ($this->IsSubmitted()) {
1659
            user_error('Can not set the currency after the order has been submitted', E_USER_NOTICE);
1660
        } else {
1661
            if (! is_a($newCurrency, Object::getCustomClass('EcommerceCurrency'))) {
1662
                $newCurrency = EcommerceCurrency::default_currency();
1663
            }
1664
            $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...
1665
            $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...
1666
            $this->write();
1667
        }
1668
    }
1669
1670
    /**
1671
     * alias for UpdateCurrency.
1672
     *
1673
     * @param EcommerceCurrency $currency
1674
     */
1675
    public function SetCurrency($currency)
1676
    {
1677
        $this->UpdateCurrency($currency);
1678
    }
1679
1680
/*******************************************************
1681
   * 5. CUSTOMER COMMUNICATION
1682
*******************************************************/
1683
1684
    /**
1685
     * Send the invoice of the order by email.
1686
     *
1687
     * @param string $emailClassName     (optional) class used to send email
1688
     * @param string $subject            (optional) subject for the email
1689
     * @param string $message            (optional) the main message in the email
1690
     * @param bool   $resend             (optional) send the email even if it has been sent before
1691
     * @param bool   $adminOnlyOrToEmail (optional) sends the email to the ADMIN ONLY, if you provide an email, it will go to the email...
1692
     *
1693
     * @return bool TRUE on success, FALSE on failure
1694
     */
1695
    public function sendEmail(
1696
        $emailClassName = 'Order_InvoiceEmail',
1697
        $subject = '',
1698
        $message = '',
1699
        $resend = false,
1700
        $adminOnlyOrToEmail = false
1701
    ) {
1702
        return $this->prepareAndSendEmail(
1703
            $emailClassName,
1704
            $subject,
1705
            $message,
1706
            $resend,
1707
            $adminOnlyOrToEmail
1708
        );
1709
    }
1710
1711
    /**
1712
     * Sends a message to the shop admin ONLY and not to the customer
1713
     * This can be used by ordersteps and orderlogs to notify the admin of any potential problems.
1714
     *
1715
     * @param string         $emailClassName       - (optional) template to be used ...
1716
     * @param string         $subject              - (optional) subject for the email
1717
     * @param string         $message              - (optional) message to be added with the email
1718
     * @param bool           $resend               - (optional) can it be sent twice?
1719
     * @param bool | string  $adminOnlyOrToEmail   - (optional) sends the email to the ADMIN ONLY, if you provide an email, it will go to the email...
1720
     *
1721
     * @return bool TRUE for success, FALSE for failure (not tested)
1722
     */
1723
    public function sendAdminNotification(
1724
        $emailClassName = 'Order_ErrorEmail',
1725
        $subject = '',
1726
        $message = '',
1727
        $resend = false,
1728
        $adminOnlyOrToEmail = true
1729
    ) {
1730
        return $this->prepareAndSendEmail(
1731
            $emailClassName,
1732
            $subject,
1733
            $message,
1734
            $resend,
1735
            $adminOnlyOrToEmail
1736
        );
1737
    }
1738
1739
    /**
1740
     * returns the order formatted as an email.
1741
     *
1742
     * @param string $emailClassName - template to use.
1743
     * @param string $subject        - (optional) the subject (which can be used as title in email)
1744
     * @param string $message        - (optional) the additional message
1745
     *
1746
     * @return string (html)
1747
     */
1748
    public function renderOrderInEmailFormat(
1749
        $emailClassName,
1750
        $subject = '',
1751
        $message = ''
1752
    )
1753
    {
1754
        $arrayData = $this->createReplacementArrayForEmail($subject, $message);
1755
        Config::nest();
1756
        Config::inst()->update('SSViewer', 'theme_enabled', true);
1757
        $html = $arrayData->renderWith($emailClassName);
1758
        Config::unnest();
1759
1760
        return Order_Email::emogrify_html($html);
1761
    }
1762
1763
    /**
1764
     * Send a mail of the order to the client (and another to the admin).
1765
     *
1766
     * @param string         $emailClassName       - (optional) template to be used ...
1767
     * @param string         $subject              - (optional) subject for the email
1768
     * @param string         $message              - (optional) message to be added with the email
1769
     * @param bool           $resend               - (optional) can it be sent twice?
1770
     * @param bool | string  $adminOnlyOrToEmail   - (optional) sends the email to the ADMIN ONLY, if you provide an email, it will go to the email...
1771
     *
1772
     * @return bool TRUE for success, FALSE for failure (not tested)
1773
     */
1774
    protected function prepareAndSendEmail(
1775
        $emailClassName,
1776
        $subject,
1777
        $message,
1778
        $resend = false,
1779
        $adminOnlyOrToEmail = false
1780
    ) {
1781
        $arrayData = $this->createReplacementArrayForEmail($subject, $message);
1782
        $from = Order_Email::get_from_email();
1783
        //why are we using this email and NOT the member.EMAIL?
1784
        //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...
1785
        if ($adminOnlyOrToEmail) {
1786
            if (filter_var($adminOnlyOrToEmail, FILTER_VALIDATE_EMAIL)) {
1787
                $to = $adminOnlyOrToEmail;
1788
                // invalid e-mail address
1789
            } else {
1790
                $to = Order_Email::get_from_email();
1791
            }
1792
        } else {
1793
            $to = $this->getOrderEmail();
1794
        }
1795
        if ($from && $to) {
1796
            $email = new $emailClassName();
1797
            if (!(is_a($email, Object::getCustomClass('Email')))) {
1798
                user_error('No correct email class provided.', E_USER_ERROR);
1799
            }
1800
            $email->setFrom($from);
1801
            $email->setTo($to);
1802
            //we take the subject from the Array Data, just in case it has been adjusted.
1803
            $email->setSubject($arrayData->getField('Subject'));
1804
            //we also see if a CC and a BCC have been added
1805
            ;
1806
            if ($cc = $arrayData->getField('CC')) {
1807
                $email->setCc($cc);
1808
            }
1809
            if ($bcc = $arrayData->getField('BCC')) {
1810
                $email->setBcc($bcc);
1811
            }
1812
            $email->populateTemplate($arrayData);
1813
            // This might be called from within the CMS,
1814
            // so we need to restore the theme, just in case
1815
            // templates within the theme exist
1816
            Config::nest();
1817
            Config::inst()->update('SSViewer', 'theme_enabled', true);
1818
            $email->setOrder($this);
1819
            $email->setResend($resend);
1820
            $result = $email->send(null);
1821
            Config::unnest();
1822
            if (Director::isDev()) {
1823
                return true;
1824
            } else {
1825
                return $result;
1826
            }
1827
        }
1828
1829
        return false;
1830
    }
1831
1832
    /**
1833
     * returns the Data that can be used in the body of an order Email
1834
     * we add the subject here so that the subject, for example, can be added to the <title>
1835
     * of the email template.
1836
     * we add the subject here so that the subject, for example, can be added to the <title>
1837
     * of the email template.
1838
     *
1839
     * @param string $subject  - (optional) subject for email
1840
     * @param string $message  - (optional) the additional message
1841
     *
1842
     * @return ArrayData
1843
     *                   - Subject - EmailSubject
1844
     *                   - Message - specific message for this order
1845
     *                   - Message - custom message
1846
     *                   - OrderStepMessage - generic message for step
1847
     *                   - Order
1848
     *                   - EmailLogo
1849
     *                   - ShopPhysicalAddress
1850
     *                   - CurrentDateAndTime
1851
     *                   - BaseURL
1852
     *                   - CC
1853
     *                   - BCC
1854
     */
1855
    protected function createReplacementArrayForEmail($subject = '', $message = '')
1856
    {
1857
        $step = $this->MyStep();
1858
        $config = $this->EcomConfig();
1859
        $replacementArray = array();
1860
        //set subject
1861
        if ( ! $subject) {
1862
            $subject = $step->EmailSubject;
1863
        }
1864
        if( ! $message) {
1865
            $message = $step->CustomerMessage;
1866
        }
1867
        $subject = str_replace('[OrderNumber]', $this->ID, $subject);
1868
        //set other variables
1869
        $replacementArray['Subject'] = $subject;
1870
        $replacementArray['To'] = '';
1871
        $replacementArray['CC'] = '';
1872
        $replacementArray['BCC'] = '';
1873
        $replacementArray['OrderStepMessage'] = $message;
1874
        $replacementArray['Order'] = $this;
1875
        $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...
1876
        $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...
1877
        $replacementArray['CurrentDateAndTime'] = DBField::create_field('SS_Datetime', 'Now');
1878
        $replacementArray['BaseURL'] = Director::baseURL();
1879
        $arrayData = ArrayData::create($replacementArray);
1880
        $this->extend('updateReplacementArrayForEmail', $arrayData);
1881
1882
        return $arrayData;
1883
    }
1884
1885
/*******************************************************
1886
   * 6. ITEM MANAGEMENT
1887
*******************************************************/
1888
1889
    /**
1890
     * returns a list of Order Attributes by type.
1891
     *
1892
     * @param array | String $types
1893
     *
1894
     * @return ArrayList
1895
     */
1896
    public function getOrderAttributesByType($types)
1897
    {
1898
        if (!is_array($types) && is_string($types)) {
1899
            $types = array($types);
1900
        }
1901
        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...
1902
            user_error('wrong parameter (types) provided in Order::getOrderAttributesByTypes');
1903
        }
1904
        $al = new ArrayList();
1905
        $items = $this->Items();
1906
        foreach ($items as $item) {
1907
            if (in_array($item->OrderAttributeType(), $types)) {
1908
                $al->push($item);
1909
            }
1910
        }
1911
        $modifiers = $this->Modifiers();
1912
        foreach ($modifiers as $modifier) {
1913
            if (in_array($modifier->OrderAttributeType(), $types)) {
1914
                $al->push($modifier);
1915
            }
1916
        }
1917
1918
        return $al;
1919
    }
1920
1921
    /**
1922
     * Returns the items of the order.
1923
     * Items are the order items (products) and NOT the modifiers (discount, tax, etc...).
1924
     *
1925
     * N. B. this method returns Order Items
1926
     * also see Buaybles
1927
1928
     *
1929
     * @param string filter - where statement to exclude certain items OR ClassName (e.g. 'TaxModifier')
1930
     *
1931
     * @return DataList (OrderItems)
1932
     */
1933
    public function Items($filterOrClassName = '')
1934
    {
1935
        if (!$this->exists()) {
1936
            $this->write();
1937
        }
1938
1939
        return $this->itemsFromDatabase($filterOrClassName);
1940
    }
1941
1942
    /**
1943
     * @alias function of Items
1944
     *
1945
     * N. B. this method returns Order Items
1946
     * also see Buaybles
1947
     *
1948
     * @param string filter - where statement to exclude certain items.
1949
     * @alias for Items
1950
     * @return DataList (OrderItems)
1951
     */
1952
    public function OrderItems($filterOrClassName = '')
1953
    {
1954
        return $this->Items($filterOrClassName);
1955
    }
1956
1957
    /**
1958
     * returns the buyables asscoiated with the order items.
1959
     *
1960
     * NB. this method retursn buyables
1961
     *
1962
     * @param string filter - where statement to exclude certain items.
1963
     *
1964
     * @return ArrayList (Buyables)
1965
     */
1966
    public function Buyables($filterOrClassName = '')
1967
    {
1968
        $items = $this->Items($filterOrClassName);
1969
        $arrayList = new ArrayList();
1970
        foreach ($items as $item) {
1971
            $arrayList->push($item->Buyable());
1972
        }
1973
1974
        return $arrayList;
1975
    }
1976
1977
    /**
1978
     * Return all the {@link OrderItem} instances that are
1979
     * available as records in the database.
1980
     *
1981
     * @param string filter - where statement to exclude certain items,
1982
     *   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)
1983
     *
1984
     * @return DataList (OrderItems)
1985
     */
1986
    protected function itemsFromDatabase($filterOrClassName = '')
1987
    {
1988
        $className = 'OrderItem';
1989
        $extrafilter = '';
1990
        if ($filterOrClassName) {
1991
            if (class_exists($filterOrClassName)) {
1992
                $className = $filterOrClassName;
1993
            } else {
1994
                $extrafilter = " AND $filterOrClassName";
1995
            }
1996
        }
1997
1998
        return $className::get()->filter(array('OrderID' => $this->ID))->where($extrafilter);
1999
    }
2000
2001
    /**
2002
     * @alias for Modifiers
2003
     *
2004
     * @return DataList (OrderModifiers)
2005
     */
2006
    public function OrderModifiers()
2007
    {
2008
        return $this->Modifiers();
2009
    }
2010
2011
    /**
2012
     * Returns the modifiers of the order, if it hasn't been saved yet
2013
     * it returns the modifiers from session, if it has, it returns them
2014
     * from the DB entry. ONLY USE OUTSIDE ORDER.
2015
     *
2016
     * @param string filter - where statement to exclude certain items OR ClassName (e.g. 'TaxModifier')
2017
     *
2018
     * @return DataList (OrderModifiers)
2019
     */
2020
    public function Modifiers($filterOrClassName = '')
2021
    {
2022
        return $this->modifiersFromDatabase($filterOrClassName);
2023
    }
2024
2025
    /**
2026
     * Get all {@link OrderModifier} instances that are
2027
     * available as records in the database.
2028
     * NOTE: includes REMOVED Modifiers, so that they do not get added again...
2029
     *
2030
     * @param string filter - where statement to exclude certain items OR ClassName (e.g. 'TaxModifier')
2031
     *
2032
     * @return DataList (OrderModifiers)
2033
     */
2034
    protected function modifiersFromDatabase($filterOrClassName = '')
2035
    {
2036
        $className = 'OrderModifier';
2037
        $extrafilter = '';
2038
        if ($filterOrClassName) {
2039
            if (class_exists($filterOrClassName)) {
2040
                $className = $filterOrClassName;
2041
            } else {
2042
                $extrafilter = " AND $filterOrClassName";
2043
            }
2044
        }
2045
2046
        return $className::get()->where('"OrderAttribute"."OrderID" = '.$this->ID." $extrafilter");
2047
    }
2048
2049
    /**
2050
     * Calculates and updates all the order attributes.
2051
     *
2052
     * @param bool $recalculate - run it, even if it has run already
2053
     */
2054
    public function calculateOrderAttributes($recalculate = false)
2055
    {
2056
        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...
2057
            //submitted orders are NEVER recalculated.
2058
            //they are set in stone.
2059
        } elseif (self::get_needs_recalculating($this->ID) || $recalculate) {
2060
            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...
2061
                $this->ensureCorrectExchangeRate();
2062
                $this->calculateOrderItems($recalculate);
2063
                $this->calculateModifiers($recalculate);
2064
                $this->extend('onCalculateOrder');
2065
            }
2066
        }
2067
    }
2068
2069
    /**
2070
     * Calculates and updates all the product items.
2071
     *
2072
     * @param bool $recalculate - run it, even if it has run already
2073
     */
2074
    protected function calculateOrderItems($recalculate = false)
2075
    {
2076
        //check if order has modifiers already
2077
        //check /re-add all non-removable ones
2078
        //$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...
2079
        $orderItems = $this->itemsFromDatabase();
2080
        if ($orderItems->count()) {
2081
            foreach ($orderItems as $orderItem) {
2082
                if ($orderItem) {
2083
                    $orderItem->runUpdate($recalculate);
2084
                }
2085
            }
2086
        }
2087
        $this->extend('onCalculateOrderItems', $orderItems);
2088
    }
2089
2090
    /**
2091
     * Calculates and updates all the modifiers.
2092
     *
2093
     * @param bool $recalculate - run it, even if it has run already
2094
     */
2095
    protected function calculateModifiers($recalculate = false)
2096
    {
2097
        $createdModifiers = $this->modifiersFromDatabase();
2098
        if ($createdModifiers->count()) {
2099
            foreach ($createdModifiers as $modifier) {
2100
                if ($modifier) {
2101
                    $modifier->runUpdate($recalculate);
2102
                }
2103
            }
2104
        }
2105
        $this->extend('onCalculateModifiers', $createdModifiers);
2106
    }
2107
2108
    /**
2109
     * Returns the subtotal of the modifiers for this order.
2110
     * If a modifier appears in the excludedModifiers array, it is not counted.
2111
     *
2112
     * @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...
2113
     * @param bool         $stopAtExcludedModifier - when this flag is TRUE, we stop adding the modifiers when we reach an excluded modifier.
2114
     *
2115
     * @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...
2116
     */
2117
    public function ModifiersSubTotal($excluded = null, $stopAtExcludedModifier = false)
2118
    {
2119
        $total = 0;
2120
        $modifiers = $this->Modifiers();
2121
        if ($modifiers->count()) {
2122
            foreach ($modifiers as $modifier) {
2123
                if (!$modifier->IsRemoved()) { //we just double-check this...
2124
                    if (is_array($excluded) && in_array($modifier->ClassName, $excluded)) {
2125
                        if ($stopAtExcludedModifier) {
2126
                            break;
2127
                        }
2128
                        //do the next modifier
2129
                        continue;
2130
                    } elseif (is_string($excluded) && ($modifier->ClassName == $excluded)) {
2131
                        if ($stopAtExcludedModifier) {
2132
                            break;
2133
                        }
2134
                        //do the next modifier
2135
                        continue;
2136
                    }
2137
                    $total += $modifier->CalculationTotal();
2138
                }
2139
            }
2140
        }
2141
2142
        return $total;
2143
    }
2144
2145
    /**
2146
     * returns a modifier that is an instanceof the classname
2147
     * it extends.
2148
     *
2149
     * @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...
2150
     *
2151
     * @return DataObject (OrderModifier)
2152
     **/
2153
    public function RetrieveModifier($className)
2154
    {
2155
        $modifiers = $this->Modifiers();
2156
        if ($modifiers->count()) {
2157
            foreach ($modifiers as $modifier) {
2158
                if (is_a($modifier, Object::getCustomClass($className))) {
2159
                    return $modifier;
2160
                }
2161
            }
2162
        }
2163
    }
2164
2165
/*******************************************************
2166
   * 7. CRUD METHODS (e.g. canView, canEdit, canDelete, etc...)
2167
*******************************************************/
2168
2169
    /**
2170
     * @param Member $member
2171
     *
2172
     * @return DataObject (Member)
2173
     **/
2174
     //TODO: please comment why we make use of this function
2175
    protected function getMemberForCanFunctions(Member $member = null)
2176
    {
2177
        if (!$member) {
2178
            $member = Member::currentUser();
2179
        }
2180
        if (!$member) {
2181
            $member = new Member();
2182
            $member->ID = 0;
2183
        }
2184
2185
        return $member;
2186
    }
2187
2188
    /**
2189
     * @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...
2190
     *
2191
     * @return bool
2192
     **/
2193
    public function canCreate($member = null)
2194
    {
2195
        $member = $this->getMemberForCanFunctions($member);
2196
        $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...
2197
        if ($extended !== null) {
2198
            return $extended;
2199
        }
2200
        if ($member->exists()) {
2201
            return $member->IsShopAdmin();
2202
        }
2203
    }
2204
2205
    /**
2206
     * Standard SS method - can the current member view this order?
2207
     *
2208
     * @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...
2209
     *
2210
     * @return bool
2211
     **/
2212
    public function canView($member = null)
2213
    {
2214
        if (!$this->exists()) {
2215
            return true;
2216
        }
2217
        $member = $this->getMemberForCanFunctions($member);
2218
        //check if this has been "altered" in any DataExtension
2219
        $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...
2220
        if ($extended !== null) {
2221
            return $extended;
2222
        }
2223
        //is the member is a shop admin they can always view it
2224
        if (EcommerceRole::current_member_is_shop_admin($member)) {
2225
            return true;
2226
        }
2227
2228
        //is the member is a shop assistant they can always view it
2229
        if (EcommerceRole::current_member_is_shop_assistant($member)) {
2230
            return true;
2231
        }
2232
        //if the current member OWNS the order, (s)he can always view it.
2233
        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...
2234
            return true;
2235
        }
2236
        //it is the current order
2237
        if ($this->IsInSession()) {
2238
            //we do some additional CHECKS for session hackings!
2239
            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...
2240
                //can't view the order of another member!
2241
                //shop admin exemption is already captured.
2242
                //this is always true
2243
                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...
2244
                    return false;
2245
                }
2246
            } else {
2247
                //order belongs to someone, but current user is NOT logged in...
2248
                //this is allowed!
2249
                //the reason it is allowed is because we want to be able to
2250
                //add order to non-existing member
2251
                return true;
2252
            }
2253
        }
2254
2255
        return false;
2256
    }
2257
2258
    /**
2259
     * @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...
2260
     * @return bool
2261
     */
2262
    public function canOverrideCanView($member = null)
2263
    {
2264
        if ($this->canView($member)) {
2265
            //can view overrides any concerns
2266
            return true;
2267
        } else {
2268
            $tsOrder = strtotime($this->LastEdited);
2269
            $tsNow = time();
2270
            $minutes = EcommerceConfig::get('Order', 'minutes_an_order_can_be_viewed_without_logging_in');
2271
            if ($minutes && ((($tsNow - $tsOrder) / 60) < $minutes)) {
2272
2273
                //has the order been edited recently?
2274
                return true;
2275
            } elseif ($orderStep = $this->MyStep()) {
2276
2277
                // order is being processed ...
2278
                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...
2279
            }
2280
        }
2281
        return false;
2282
    }
2283
2284
    /**
2285
     * @return bool
2286
     */
2287
    public function IsInSession()
2288
    {
2289
        $orderInSession = ShoppingCart::session_order();
2290
2291
        return $orderInSession && $this->ID && $this->ID == $orderInSession->ID;
2292
    }
2293
2294
    /**
2295
     * returns a pseudo random part of the session id.
2296
     *
2297
     * @param int $size
2298
     *
2299
     * @return string
2300
     */
2301
    public function LessSecureSessionID($size = 7, $start = null)
2302
    {
2303
        if (!$start || $start < 0 || $start > (32 - $size)) {
2304
            $start = 0;
2305
        }
2306
2307
        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...
2308
    }
2309
    /**
2310
     *
2311
     * @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...
2312
     *
2313
     * @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...
2314
     **/
2315
    public function canViewAdminStuff($member = null)
2316
    {
2317
        $member = $this->getMemberForCanFunctions($member);
2318
        $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...
2319
        if ($extended !== null) {
2320
            return $extended;
2321
        }
2322
        if (Permission::checkMember($member, Config::inst()->get('EcommerceRole', 'admin_permission_code'))) {
2323
            return true;
2324
        }
2325
    }
2326
2327
    /**
2328
     * if we set canEdit to false then we
2329
     * can not see the child records
2330
     * Basically, you can edit when you can view and canEdit (even as a customer)
2331
     * Or if you are a Shop Admin you can always edit.
2332
     * Otherwise it is false...
2333
     *
2334
     * @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...
2335
     *
2336
     * @return bool
2337
     **/
2338
    public function canEdit($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->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...
2346
            return true;
2347
        }
2348
        if (Permission::checkMember($member, Config::inst()->get('EcommerceRole', 'admin_permission_code'))) {
2349
            return true;
2350
        }
2351
        //is the member is a shop assistant they can always view it
2352
        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...
2353
            return true;
2354
        }
2355
        return false;
2356
    }
2357
2358
    /**
2359
     * is the order ready to go through to the
2360
     * checkout process.
2361
     *
2362
     * This method checks all the order items and order modifiers
2363
     * If any of them need immediate attention then this is done
2364
     * first after which it will go through to the checkout page.
2365
     *
2366
     * @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...
2367
     *
2368
     * @return bool
2369
     **/
2370
    public function canCheckout(Member $member = null)
2371
    {
2372
        $member = $this->getMemberForCanFunctions($member);
2373
        $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...
2374
        if ($extended !== null) {
2375
            return $extended;
2376
        }
2377
        $submitErrors = $this->SubmitErrors();
2378
        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...
2379
            return false;
2380
        }
2381
2382
        return true;
2383
    }
2384
2385
    /**
2386
     * Can the order be submitted?
2387
     * this method can be used to stop an order from being submitted
2388
     * due to something not being completed or done.
2389
     *
2390
     * @see Order::SubmitErrors
2391
     *
2392
     * @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...
2393
     *
2394
     * @return bool
2395
     **/
2396
    public function canSubmit(Member $member = null)
2397
    {
2398
        $member = $this->getMemberForCanFunctions($member);
2399
        $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...
2400
        if ($extended !== null) {
2401
            return $extended;
2402
        }
2403
        if ($this->IsSubmitted()) {
2404
            return false;
2405
        }
2406
        $submitErrors = $this->SubmitErrors();
2407
        if ($submitErrors && $submitErrors->count()) {
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return !($submitErrors &...submitErrors->count());.
Loading history...
2408
            return false;
2409
        }
2410
2411
        return true;
2412
    }
2413
2414
    /**
2415
     * Can a payment be made for this Order?
2416
     *
2417
     * @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...
2418
     *
2419
     * @return bool
2420
     **/
2421
    public function canPay(Member $member = null)
2422
    {
2423
        $member = $this->getMemberForCanFunctions($member);
2424
        $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...
2425
        if ($extended !== null) {
2426
            return $extended;
2427
        }
2428
        if ($this->IsPaid() || $this->IsCancelled() || $this->PaymentIsPending()) {
2429
            return false;
2430
        }
2431
2432
        return $this->MyStep()->CustomerCanPay;
2433
    }
2434
2435
    /**
2436
     * Can the given member cancel this order?
2437
     *
2438
     * @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...
2439
     *
2440
     * @return bool
2441
     **/
2442
    public function canCancel(Member $member = null)
2443
    {
2444
        //if it is already cancelled it can not be cancelled again
2445
        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...
2446
            return false;
2447
        }
2448
        $member = $this->getMemberForCanFunctions($member);
2449
        $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...
2450
        if ($extended !== null) {
2451
            return $extended;
2452
        }
2453
        if (EcommerceRole::current_member_can_process_orders($member)) {
2454
            return true;
2455
        }
2456
2457
        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...
2458
    }
2459
2460
    /**
2461
     * @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...
2462
     *
2463
     * @return bool
2464
     **/
2465
    public function canDelete($member = null)
2466
    {
2467
        $member = $this->getMemberForCanFunctions($member);
2468
        $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...
2469
        if ($extended !== null) {
2470
            return $extended;
2471
        }
2472
        if ($this->IsSubmitted()) {
2473
            return false;
2474
        }
2475
        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...
2476
            return true;
2477
        }
2478
2479
        return false;
2480
    }
2481
2482
    /**
2483
     * Returns all the order logs that the current member can view
2484
     * i.e. some order logs can only be viewed by the admin (e.g. suspected fraud orderlog).
2485
     *
2486
     * @return ArrayList (OrderStatusLogs)
2487
     **/
2488
    public function CanViewOrderStatusLogs()
2489
    {
2490
        $canViewOrderStatusLogs = new ArrayList();
2491
        $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...
2492
        foreach ($logs as $log) {
2493
            if ($log->canView()) {
2494
                $canViewOrderStatusLogs->push($log);
2495
            }
2496
        }
2497
2498
        return $canViewOrderStatusLogs;
2499
    }
2500
2501
    /**
2502
     * returns all the logs that can be viewed by the customer.
2503
     *
2504
     * @return ArrayList (OrderStausLogs)
2505
     */
2506
    public function CustomerViewableOrderStatusLogs()
2507
    {
2508
        $customerViewableOrderStatusLogs = new ArrayList();
2509
        $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...
2510
        if ($logs) {
2511
            foreach ($logs as $log) {
2512
                if (!$log->InternalUseOnly) {
2513
                    $customerViewableOrderStatusLogs->push($log);
2514
                }
2515
            }
2516
        }
2517
2518
        return $customerViewableOrderStatusLogs;
2519
    }
2520
2521
/*******************************************************
2522
   * 8. GET METHODS (e.g. Total, SubTotal, Title, etc...)
2523
*******************************************************/
2524
2525
    /**
2526
     * returns the email to be used for customer communication.
2527
     *
2528
     * @return string
2529
     */
2530
    public function OrderEmail()
2531
    {
2532
        return $this->getOrderEmail();
2533
    }
2534
    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...
2535
    {
2536
        $email = '';
2537
        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...
2538
            $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...
2539
        }
2540
        if (! $email) {
2541
            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...
2542
                $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...
2543
            }
2544
        }
2545
        $extendedEmail = $this->extend('updateOrderEmail', $email);
2546
        if ($extendedEmail !== null && is_array($extendedEmail) && count($extendedEmail)) {
2547
            $email = implode(';', $extendedEmail);
2548
        }
2549
2550
        return $email;
2551
    }
2552
2553
    /**
2554
     * Returns true if there is a prink or email link.
2555
     *
2556
     * @return bool
2557
     */
2558
    public function HasPrintOrEmailLink()
2559
    {
2560
        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...
2561
    }
2562
2563
    /**
2564
     * returns the absolute link to the order that can be used in the customer communication (email).
2565
     *
2566
     * @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...
2567
     */
2568
    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...
2569
    {
2570
        return $this->getEmailLink();
2571
    }
2572
    public function getEmailLink($type = 'Order_StatusEmail')
0 ignored issues
show
Unused Code introduced by
The parameter $type is not used and could be removed.

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

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

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
2573
    {
2574
        if (!isset($_REQUEST['print'])) {
2575
            if ($this->IsSubmitted()) {
2576
                return Director::AbsoluteURL(OrderConfirmationPage::get_email_link($this->ID, $this->MyStep()->getEmailClassName(), $actuallySendEmail = true));
2577
            }
2578
        }
2579
    }
2580
2581
    /**
2582
     * returns the absolute link to the order for printing.
2583
     *
2584
     * @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...
2585
     */
2586
    public function PrintLink()
2587
    {
2588
        return $this->getPrintLink();
2589
    }
2590
    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...
2591
    {
2592
        if (!isset($_REQUEST['print'])) {
2593
            if ($this->IsSubmitted()) {
2594
                return Director::AbsoluteURL(OrderConfirmationPage::get_order_link($this->ID)).'?print=1';
2595
            }
2596
        }
2597
    }
2598
2599
    /**
2600
     * returns the absolute link to the order for printing.
2601
     *
2602
     * @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...
2603
     */
2604
    public function PackingSlipLink()
2605
    {
2606
        return $this->getPackingSlipLink();
2607
    }
2608
    public function getPackingSlipLink()
2609
    {
2610
        if ($this->IsSubmitted()) {
2611
            return Director::AbsoluteURL(OrderConfirmationPage::get_order_link($this->ID)).'?packingslip=1';
2612
        }
2613
    }
2614
2615
    /**
2616
     * returns the absolute link that the customer can use to retrieve the email WITHOUT logging in.
2617
     *
2618
     * @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...
2619
     */
2620
    public function RetrieveLink()
2621
    {
2622
        return $this->getRetrieveLink();
2623
    }
2624
2625
    public function getRetrieveLink()
2626
    {
2627
        //important to recalculate!
2628
        if ($this->IsSubmitted($recalculate = true)) {
2629
            //add session ID if not added yet...
2630
            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...
2631
                $this->write();
2632
            }
2633
2634
            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...
2635
        } else {
2636
            return Director::AbsoluteURL('/shoppingcart/loadorder/'.$this->ID.'/');
2637
        }
2638
    }
2639
2640
    public function ShareLink()
2641
    {
2642
        return $this->getShareLink();
2643
    }
2644
2645
    public function getShareLink()
2646
    {
2647
        $orderItems = $this->itemsFromDatabase();
2648
        $action = 'share';
2649
        $array = array();
2650
        foreach ($orderItems as $orderItem) {
2651
            $array[] = implode(
2652
                ',',
2653
                array(
2654
                    $orderItem->BuyableClassName,
2655
                    $orderItem->BuyableID,
2656
                    $orderItem->Quantity
2657
                )
2658
            );
2659
        }
2660
2661
        return Director::AbsoluteURL(CartPage::find_link($action.'/'.implode('-', $array)));
2662
    }
2663
2664
    /**
2665
     * link to delete order.
2666
     *
2667
     * @return string
2668
     */
2669
    public function DeleteLink()
2670
    {
2671
        return $this->getDeleteLink();
2672
    }
2673
    public function getDeleteLink()
2674
    {
2675
        if ($this->canDelete()) {
2676
            return ShoppingCart_Controller::delete_order_link($this->ID);
2677
        } else {
2678
            return '';
2679
        }
2680
    }
2681
2682
    /**
2683
     * link to copy order.
2684
     *
2685
     * @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...
2686
     */
2687
    public function CopyOrderLink()
2688
    {
2689
        return $this->getCopyOrderLink();
2690
    }
2691
    public function getCopyOrderLink()
2692
    {
2693
        if ($this->canView() && $this->IsSubmitted()) {
2694
            return ShoppingCart_Controller::copy_order_link($this->ID);
2695
        } else {
2696
            return '';
2697
        }
2698
    }
2699
2700
    /**
2701
     * A "Title" for the order, which summarises the main details (date, and customer) in a string.
2702
     *
2703
     * @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...
2704
     * @param bool   $includeName - e.g. by Mr Johnson
2705
     *
2706
     * @return string
2707
     **/
2708
    public function Title($dateFormat = null, $includeName = false)
2709
    {
2710
        return $this->getTitle($dateFormat, $includeName);
2711
    }
2712
    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...
2713
    {
2714
        if ($this->exists()) {
2715
            if ($dateFormat === null) {
2716
                $dateFormat = EcommerceConfig::get('Order', 'date_format_for_title');
2717
            }
2718
            if ($includeName === null) {
2719
                $includeName = EcommerceConfig::get('Order', 'include_customer_name_in_title');
2720
            }
2721
            $title = $this->i18n_singular_name()." #".number_format($this->ID);
2722
            if ($dateFormat) {
2723
                if ($submissionLog = $this->SubmissionLog()) {
2724
                    $dateObject = $submissionLog->dbObject('Created');
2725
                    $placed = _t('Order.PLACED', 'placed');
2726
                } else {
2727
                    $dateObject = $this->dbObject('Created');
2728
                    $placed = _t('Order.STARTED', 'started');
2729
                }
2730
                $title .= ' - '.$placed.' '.$dateObject->Format($dateFormat);
2731
            }
2732
            $name = '';
2733
            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...
2734
                $name = ' - '._t('Order.CANCELLED', 'CANCELLED');
2735
            }
2736
            if ($includeName) {
2737
                $by = _t('Order.BY', 'by');
2738
                if (!$name) {
2739
                    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...
2740
                        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...
2741
                            $name = ' - '.$by.' '.$billingAddress->Prefix.' '.$billingAddress->FirstName.' '.$billingAddress->Surname;
2742
                        }
2743
                    }
2744
                }
2745
                if (!$name) {
2746
                    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...
2747
                        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...
2748
                            if ($member->exists()) {
2749
                                if ($memberName = $member->getName()) {
2750
                                    if (!trim($memberName)) {
2751
                                        $memberName = _t('Order.ANONYMOUS', 'anonymous');
2752
                                    }
2753
                                    $name = ' - '.$by.' '.$memberName;
2754
                                }
2755
                            }
2756
                        }
2757
                    }
2758
                }
2759
            }
2760
            $title .= $name;
2761
        } else {
2762
            $title = _t('Order.NEW', 'New').' '.$this->i18n_singular_name();
2763
        }
2764
        $extendedTitle = $this->extend('updateTitle', $title);
2765
        if ($extendedTitle !== null && is_array($extendedTitle) && count($extendedTitle)) {
2766
            $title = implode('; ', $extendedTitle);
2767
        }
2768
2769
        return $title;
2770
    }
2771
2772
    /**
2773
     * Returns the subtotal of the items for this order.
2774
     *
2775
     * @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...
2776
     */
2777
    public function SubTotal()
2778
    {
2779
        return $this->getSubTotal();
2780
    }
2781
    public function getSubTotal()
2782
    {
2783
        $result = 0;
2784
        $items = $this->Items();
2785
        if ($items->count()) {
2786
            foreach ($items as $item) {
2787
                if (is_a($item, Object::getCustomClass('OrderAttribute'))) {
2788
                    $result += $item->Total();
2789
                }
2790
            }
2791
        }
2792
2793
        return $result;
2794
    }
2795
2796
    /**
2797
     * @return Currency (DB Object)
2798
     **/
2799
    public function SubTotalAsCurrencyObject()
2800
    {
2801
        return DBField::create_field('Currency', $this->SubTotal());
2802
    }
2803
2804
    /**
2805
     * @return Money
2806
     **/
2807
    public function SubTotalAsMoney()
2808
    {
2809
        return $this->getSubTotalAsMoney();
2810
    }
2811
    public function getSubTotalAsMoney()
2812
    {
2813
        return EcommerceCurrency::get_money_object_from_order_currency($this->SubTotal(), $this);
2814
    }
2815
2816
    /**
2817
     * @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...
2818
     * @param bool         $stopAtExcludedModifier - when this flag is TRUE, we stop adding the modifiers when we reach an excluded modifier.
2819
     *
2820
     * @return Currency (DB Object)
2821
     **/
2822
    public function ModifiersSubTotalAsCurrencyObject($excluded = null, $stopAtExcludedModifier = false)
2823
    {
2824
        return DBField::create_field('Currency', $this->ModifiersSubTotal($excluded, $stopAtExcludedModifier));
2825
    }
2826
2827
    /**
2828
     * @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...
2829
     * @param bool         $stopAtExcludedModifier - when this flag is TRUE, we stop adding the modifiers when we reach an excluded modifier.
2830
     *
2831
     * @return Money (DB Object)
2832
     **/
2833
    public function ModifiersSubTotalAsMoneyObject($excluded = null, $stopAtExcludedModifier = false)
2834
    {
2835
        return EcommerceCurrency::get_money_object_from_order_currency($this->ModifiersSubTotal($excluded, $stopAtExcludedModifier), $this);
2836
    }
2837
2838
    /**
2839
     * Returns the total cost of an order including the additional charges or deductions of its modifiers.
2840
     *
2841
     * @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...
2842
     */
2843
    public function Total()
2844
    {
2845
        return $this->getTotal();
2846
    }
2847
    public function getTotal()
2848
    {
2849
        return $this->SubTotal() + $this->ModifiersSubTotal();
2850
    }
2851
2852
    /**
2853
     * @return Currency (DB Object)
2854
     **/
2855
    public function TotalAsCurrencyObject()
2856
    {
2857
        return DBField::create_field('Currency', $this->Total());
2858
    }
2859
2860
    /**
2861
     * @return Money
2862
     **/
2863
    public function TotalAsMoney()
2864
    {
2865
        return $this->getTotalAsMoney();
2866
    }
2867
    public function getTotalAsMoney()
2868
    {
2869
        return EcommerceCurrency::get_money_object_from_order_currency($this->Total(), $this);
2870
    }
2871
2872
    /**
2873
     * Checks to see if any payments have been made on this order
2874
     * and if so, subracts the payment amount from the order.
2875
     *
2876
     * @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...
2877
     **/
2878
    public function TotalOutstanding()
2879
    {
2880
        return $this->getTotalOutstanding();
2881
    }
2882
    public function getTotalOutstanding()
2883
    {
2884
        if ($this->IsSubmitted()) {
2885
            $total = $this->Total();
2886
            $paid = $this->TotalPaid();
2887
            $outstanding = $total - $paid;
2888
            $maxDifference = EcommerceConfig::get('Order', 'maximum_ignorable_sales_payments_difference');
2889
            if (abs($outstanding) < $maxDifference) {
2890
                $outstanding = 0;
2891
            }
2892
2893
            return floatval($outstanding);
2894
        } else {
2895
            return 0;
2896
        }
2897
    }
2898
2899
    /**
2900
     * @return Currency (DB Object)
2901
     **/
2902
    public function TotalOutstandingAsCurrencyObject()
2903
    {
2904
        return DBField::create_field('Currency', $this->TotalOutstanding());
2905
    }
2906
2907
    /**
2908
     * @return Money
2909
     **/
2910
    public function TotalOutstandingAsMoney()
2911
    {
2912
        return $this->getTotalOutstandingAsMoney();
2913
    }
2914
    public function getTotalOutstandingAsMoney()
2915
    {
2916
        return EcommerceCurrency::get_money_object_from_order_currency($this->TotalOutstanding(), $this);
2917
    }
2918
2919
    /**
2920
     * @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...
2921
     */
2922
    public function TotalPaid()
2923
    {
2924
        return $this->getTotalPaid();
2925
    }
2926
    public function getTotalPaid()
2927
    {
2928
        $paid = 0;
2929
        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...
2930
            foreach ($payments as $payment) {
2931
                if ($payment->Status == 'Success') {
2932
                    $paid += $payment->Amount->getAmount();
2933
                }
2934
            }
2935
        }
2936
        $reverseExchange = 1;
2937
        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...
2938
            $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...
2939
        }
2940
2941
        return $paid * $reverseExchange;
2942
    }
2943
2944
    /**
2945
     * @return Currency (DB Object)
2946
     **/
2947
    public function TotalPaidAsCurrencyObject()
2948
    {
2949
        return DBField::create_field('Currency', $this->TotalPaid());
2950
    }
2951
2952
    /**
2953
     * @return Money
2954
     **/
2955
    public function TotalPaidAsMoney()
2956
    {
2957
        return $this->getTotalPaidAsMoney();
2958
    }
2959
    public function getTotalPaidAsMoney()
2960
    {
2961
        return EcommerceCurrency::get_money_object_from_order_currency($this->TotalPaid(), $this);
2962
    }
2963
2964
    /**
2965
     * returns the total number of OrderItems (not modifiers).
2966
     * This is meant to run as fast as possible to quickly check
2967
     * if there is anything in the cart.
2968
     *
2969
     * @param bool $recalculate - do we need to recalculate (value is retained during lifetime of Object)
2970
     *
2971
     * @return int
2972
     **/
2973
    public function TotalItems($recalculate = false)
2974
    {
2975
        return $this->getTotalItems($recalculate);
2976
    }
2977
    public function getTotalItems($recalculate = false)
2978
    {
2979
        if ($this->totalItems === null || $recalculate) {
2980
            $this->totalItems = OrderItem::get()
2981
                ->where('"OrderAttribute"."OrderID" = '.$this->ID.' AND "OrderItem"."Quantity" > 0')
2982
                ->count();
2983
        }
2984
2985
        return $this->totalItems;
2986
    }
2987
2988
    /**
2989
     * Little shorthand.
2990
     *
2991
     * @param bool $recalculate
2992
     *
2993
     * @return bool
2994
     **/
2995
    public function MoreThanOneItemInCart($recalculate = false)
2996
    {
2997
        return $this->TotalItems($recalculate) > 1 ? true : false;
2998
    }
2999
3000
    /**
3001
     * returns the total number of OrderItems (not modifiers) times their respectective quantities.
3002
     *
3003
     * @param bool $recalculate - force recalculation
3004
     *
3005
     * @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...
3006
     **/
3007
    public function TotalItemsTimesQuantity($recalculate = false)
3008
    {
3009
        return $this->getTotalItemsTimesQuantity($recalculate);
3010
    }
3011
    public function getTotalItemsTimesQuantity($recalculate = false)
3012
    {
3013
        if ($this->totalItemsTimesQuantity === null || $recalculate) {
3014
            //to do, why do we check if you can edit ????
3015
            $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...
3016
                SELECT SUM("OrderItem"."Quantity")
3017
                FROM "OrderItem"
3018
                    INNER JOIN "OrderAttribute" ON "OrderAttribute"."ID" = "OrderItem"."ID"
3019
                WHERE
3020
                    "OrderAttribute"."OrderID" = '.$this->ID.'
3021
                    AND "OrderItem"."Quantity" > 0'
3022
            )->value();
3023
        }
3024
3025
        return $this->totalItemsTimesQuantity - 0;
3026
    }
3027
3028
    /**
3029
     *
3030
     * @return string (country code)
3031
     **/
3032
    public function Country()
3033
    {
3034
        return $this->getCountry();
3035
    }
3036
3037
    /**
3038
    * Returns the country code for the country that applies to the order.
3039
    * @alias  for getCountry
3040
    *
3041
    * @return string - country code e.g. NZ
3042
     */
3043
    public function getCountry()
3044
    {
3045
        $countryCodes = array(
3046
            'Billing' => '',
3047
            'Shipping' => '',
3048
        );
3049
        $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...
3050
        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...
3051
            $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...
3052
            if ($billingAddress) {
3053
                if ($billingAddress->Country) {
3054
                    $countryCodes['Billing'] = $billingAddress->Country;
3055
                }
3056
            }
3057
        }
3058
        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...
3059
            $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...
3060
            if ($shippingAddress) {
3061
                if ($shippingAddress->ShippingCountry) {
3062
                    $countryCodes['Shipping'] = $shippingAddress->ShippingCountry;
3063
                }
3064
            }
3065
        }
3066
        if (
3067
            (EcommerceConfig::get('OrderAddress', 'use_shipping_address_for_main_region_and_country') && $countryCodes['Shipping'])
3068
            ||
3069
            (!$countryCodes['Billing'] && $countryCodes['Shipping'])
3070
        ) {
3071
            $code = $countryCodes['Shipping'];
3072
        } elseif ($countryCodes['Billing']) {
3073
            $code = $countryCodes['Billing'];
3074
        } else {
3075
            $code = EcommerceCountry::get_country_from_ip();
3076
        }
3077
3078
        return $code;
3079
    }
3080
3081
    /**
3082
     * @alias for getFullNameCountry
3083
     *
3084
     * @return string - country name
3085
     **/
3086
    public function FullNameCountry()
3087
    {
3088
        return $this->getFullNameCountry();
3089
    }
3090
3091
    /**
3092
     * returns name of coutry.
3093
     *
3094
     * @return string - country name
3095
     **/
3096
    public function getFullNameCountry()
3097
    {
3098
        return EcommerceCountry::find_title($this->Country());
3099
    }
3100
3101
    /**
3102
     * @alis for getExpectedCountryName
3103
     * @return string - country name
3104
     **/
3105
    public function ExpectedCountryName()
3106
    {
3107
        return $this->getExpectedCountryName();
3108
    }
3109
3110
    /**
3111
     * returns name of coutry that we expect the customer to have
3112
     * This takes into consideration more than just what has been entered
3113
     * for example, it looks at GEO IP.
3114
     *
3115
     * @todo: why do we dont return a string IF there is only one item.
3116
     *
3117
     * @return string - country name
3118
     **/
3119
    public function getExpectedCountryName()
3120
    {
3121
        return EcommerceCountry::find_title(EcommerceCountry::get_country(false, $this->ID));
3122
    }
3123
3124
    /**
3125
     * return the title of the fixed country (if any).
3126
     *
3127
     * @return string | empty string
3128
     **/
3129
    public function FixedCountry()
3130
    {
3131
        return $this->getFixedCountry();
3132
    }
3133
    public function getFixedCountry()
3134
    {
3135
        $code = EcommerceCountry::get_fixed_country_code();
3136
        if ($code) {
3137
            return EcommerceCountry::find_title($code);
3138
        }
3139
3140
        return '';
3141
    }
3142
3143
    /**
3144
     * Returns the region that applies to the order.
3145
     * we check both billing and shipping, in case one of them is empty.
3146
     *
3147
     * @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...
3148
     **/
3149
    public function Region()
3150
    {
3151
        return $this->getRegion();
3152
    }
3153
    public function getRegion()
3154
    {
3155
        $regionIDs = array(
3156
            'Billing' => 0,
3157
            'Shipping' => 0,
3158
        );
3159
        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...
3160
            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...
3161
                if ($billingAddress->RegionID) {
3162
                    $regionIDs['Billing'] = $billingAddress->RegionID;
3163
                }
3164
            }
3165
        }
3166
        if ($this->CanHaveShippingAddress()) {
3167
            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...
3168
                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...
3169
                    if ($shippingAddress->ShippingRegionID) {
3170
                        $regionIDs['Shipping'] = $shippingAddress->ShippingRegionID;
3171
                    }
3172
                }
3173
            }
3174
        }
3175
        if (count($regionIDs)) {
3176
            //note the double-check with $this->CanHaveShippingAddress() and get_use_....
3177
            if ($this->CanHaveShippingAddress() && EcommerceConfig::get('OrderAddress', 'use_shipping_address_for_main_region_and_country') && $regionIDs['Shipping']) {
3178
                return EcommerceRegion::get()->byID($regionIDs['Shipping']);
3179
            } else {
3180
                return EcommerceRegion::get()->byID($regionIDs['Billing']);
3181
            }
3182
        } else {
3183
            return EcommerceRegion::get()->byID(EcommerceRegion::get_region_from_ip());
3184
        }
3185
    }
3186
3187
    /**
3188
     * Casted variable
3189
     * Currency is not the same as the standard one?
3190
     *
3191
     * @return bool
3192
     **/
3193
    public function HasAlternativeCurrency()
3194
    {
3195
        return $this->getHasAlternativeCurrency();
3196
    }
3197
    public function getHasAlternativeCurrency()
3198
    {
3199
        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...
3200
            if ($currency->IsDefault()) {
0 ignored issues
show
Coding Style introduced by
The if-else statement can be simplified to return !$currency->IsDefault();.
Loading history...
3201
                return false;
3202
            } else {
3203
                return true;
3204
            }
3205
        } else {
3206
            return false;
3207
        }
3208
    }
3209
3210
    /**
3211
     * Makes sure exchange rate is updated and maintained before order is submitted
3212
     * This method is public because it could be called from a shopping Cart Object.
3213
     **/
3214
    public function EnsureCorrectExchangeRate()
3215
    {
3216
        if (!$this->IsSubmitted()) {
3217
            $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...
3218
            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...
3219
                if ($currency->IsDefault()) {
3220
                    $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...
3221
                } else {
3222
                    $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...
3223
                }
3224
            } else {
3225
                $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...
3226
            }
3227
            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...
3228
                $this->write();
3229
            }
3230
        }
3231
    }
3232
3233
    /**
3234
     * speeds up processing by storing the IsSubmitted value
3235
     * we start with -1 to know if it has been requested before.
3236
     *
3237
     * @var bool
3238
     */
3239
    protected $_isSubmittedTempVar = -1;
3240
3241
    /**
3242
     * Casted variable - has the order been submitted?
3243
     * alias
3244
     * @param bool $recalculate
3245
     *
3246
     * @return bool
3247
     **/
3248
    public function IsSubmitted($recalculate = true)
3249
    {
3250
        return $this->getIsSubmitted($recalculate);
3251
    }
3252
3253
    /**
3254
     * Casted variable - has the order been submitted?
3255
     *
3256
     * @param bool $recalculate
3257
     *
3258
     * @return bool
3259
     **/
3260
    public function getIsSubmitted($recalculate = false)
3261
    {
3262
        if ($this->_isSubmittedTempVar === -1 || $recalculate) {
3263
            if ($this->SubmissionLog()) {
3264
                $this->_isSubmittedTempVar = true;
3265
            } else {
3266
                $this->_isSubmittedTempVar = false;
3267
            }
3268
        }
3269
3270
        return $this->_isSubmittedTempVar;
3271
    }
3272
3273
    /**
3274
     *
3275
     *
3276
     * @return bool
3277
     */
3278
    public function IsArchived()
3279
    {
3280
        $lastStep = OrderStep::get()->Last();
3281
        if ($lastStep) {
3282
            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...
3283
                return true;
3284
            }
3285
        }
3286
        return false;
3287
    }
3288
3289
    /**
3290
     * Submission Log for this Order (if any).
3291
     *
3292
     * @return Submission Log (OrderStatusLog_Submitted) | Null
3293
     **/
3294
    public function SubmissionLog()
3295
    {
3296
        $className = EcommerceConfig::get('OrderStatusLog', 'order_status_log_class_used_for_submitting_order');
3297
3298
        return $className::get()
3299
            ->Filter(array('OrderID' => $this->ID))
3300
            ->Last();
3301
    }
3302
3303
    /**
3304
     * @return int
3305
     */
3306
    public function SecondsSinceBeingSubmitted()
3307
    {
3308
        if ($submissionLog = $this->SubmissionLog()) {
3309
            return time() - strtotime($submissionLog->Created);
3310
        } else {
3311
            return 0;
3312
        }
3313
    }
3314
3315
    /**
3316
     * if the order can not be submitted,
3317
     * then the reasons why it can not be submitted
3318
     * will be returned by this method.
3319
     *
3320
     * @see Order::canSubmit
3321
     *
3322
     * @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...
3323
     */
3324
    public function SubmitErrors()
3325
    {
3326
        $al = null;
3327
        $extendedSubmitErrors = $this->extend('updateSubmitErrors');
3328
        if ($extendedSubmitErrors !== null && is_array($extendedSubmitErrors) && count($extendedSubmitErrors)) {
3329
            $al = ArrayList::create();
3330
            foreach ($extendedSubmitErrors as $returnResultArray) {
3331
                foreach ($returnResultArray as $issue) {
3332
                    if ($issue) {
3333
                        $al->push(ArrayData::create(array("Title" => $issue)));
3334
                    }
3335
                }
3336
            }
3337
        }
3338
        return $al;
3339
    }
3340
3341
    /**
3342
     * Casted variable - has the order been submitted?
3343
     *
3344
     * @param bool $withDetail
3345
     *
3346
     * @return string
3347
     **/
3348
    public function CustomerStatus($withDetail = true)
3349
    {
3350
        return $this->getCustomerStatus($withDetail);
3351
    }
3352
    public function getCustomerStatus($withDetail = true)
3353
    {
3354
        $str = '';
3355
        if ($this->MyStep()->ShowAsUncompletedOrder) {
3356
            $str = _t('Order.UNCOMPLETED', 'Uncompleted');
3357
        } elseif ($this->MyStep()->ShowAsInProcessOrder) {
3358
            $str = _t('Order.IN_PROCESS', 'In Process');
3359
        } elseif ($this->MyStep()->ShowAsCompletedOrder) {
3360
            $str = _t('Order.COMPLETED', 'Completed');
3361
        }
3362
        if ($withDetail) {
3363
            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...
3364
                $str .= ' ('.$this->MyStep()->Name.')';
3365
            }
3366
        }
3367
3368
        return $str;
3369
    }
3370
3371
    /**
3372
     * Casted variable - does the order have a potential shipping address?
3373
     *
3374
     * @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...
3375
     **/
3376
    public function CanHaveShippingAddress()
3377
    {
3378
        return $this->getCanHaveShippingAddress();
3379
    }
3380
    public function getCanHaveShippingAddress()
3381
    {
3382
        return EcommerceConfig::get('OrderAddress', 'use_separate_shipping_address');
3383
    }
3384
3385
    /**
3386
     * returns the link to view the Order
3387
     * WHY NOT CHECKOUT PAGE: first we check for cart page.
3388
     *
3389
     * @return CartPage | Null
3390
     */
3391
    public function DisplayPage()
3392
    {
3393
        if ($this->MyStep() && $this->MyStep()->AlternativeDisplayPage()) {
3394
            $page = $this->MyStep()->AlternativeDisplayPage();
3395
        } elseif ($this->IsSubmitted()) {
3396
            $page = OrderConfirmationPage::get()->First();
3397
        } else {
3398
            $page = CartPage::get()
3399
                ->Filter(array('ClassName' => 'CartPage'))
3400
                ->First();
3401
            if (!$page) {
3402
                $page = CheckoutPage::get()->First();
3403
            }
3404
        }
3405
3406
        return $page;
3407
    }
3408
3409
    /**
3410
     * returns the link to view the Order
3411
     * WHY NOT CHECKOUT PAGE: first we check for cart page.
3412
     * If a cart page has been created then we refer through to Cart Page.
3413
     * Otherwise it will default to the checkout page.
3414
     *
3415
     * @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...
3416
     *
3417
     * @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...
3418
     */
3419
    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...
3420
    {
3421
        $page = $this->DisplayPage();
3422
        if ($page) {
3423
            return $page->getOrderLink($this->ID, $action);
3424
        } else {
3425
            user_error('A Cart / Checkout Page + an Order Confirmation Page needs to be setup for the e-commerce module to work.', E_USER_NOTICE);
3426
            $page = ErrorPage::get()
3427
                ->Filter(array('ErrorCode' => '404'))
3428
                ->First();
3429
            if ($page) {
3430
                return $page->Link();
3431
            }
3432
        }
3433
    }
3434
3435
    /**
3436
     * Returns to link to access the Order's API.
3437
     *
3438
     * @param string $version
3439
     * @param string $extension
3440
     *
3441
     * @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...
3442
     */
3443
    public function APILink($version = 'v1', $extension = 'xml')
3444
    {
3445
        return Director::AbsoluteURL("/api/ecommerce/$version/Order/".$this->ID."/.$extension");
3446
    }
3447
3448
    /**
3449
     * returns the link to finalise the Order.
3450
     *
3451
     * @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...
3452
     */
3453
    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...
3454
    {
3455
        $page = CheckoutPage::get()->First();
3456
        if ($page) {
3457
            return $page->Link();
3458
        } else {
3459
            $page = ErrorPage::get()
3460
                ->Filter(array('ErrorCode' => '404'))
3461
                ->First();
3462
            if ($page) {
3463
                return $page->Link();
3464
            }
3465
        }
3466
    }
3467
3468
    /**
3469
     * Converts the Order into HTML, based on the Order Template.
3470
     *
3471
     * @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...
3472
     **/
3473
    public function ConvertToHTML()
3474
    {
3475
        Config::nest();
3476
        Config::inst()->update('SSViewer', 'theme_enabled', true);
3477
        $html = $this->renderWith('Order');
3478
        Config::unnest();
3479
        $html = preg_replace('/(\s)+/', ' ', $html);
3480
3481
        return DBField::create_field('HTMLText', $html);
3482
    }
3483
3484
    /**
3485
     * Converts the Order into a serialized string
3486
     * TO DO: check if this works and check if we need to use special sapphire serialization code.
3487
     *
3488
     * @return string - serialized object
3489
     **/
3490
    public function ConvertToString()
3491
    {
3492
        return serialize($this->addHasOneAndHasManyAsVariables());
3493
    }
3494
3495
    /**
3496
     * Converts the Order into a JSON object
3497
     * TO DO: check if this works and check if we need to use special sapphire JSON code.
3498
     *
3499
     * @return string -  JSON
3500
     **/
3501
    public function ConvertToJSON()
3502
    {
3503
        return json_encode($this->addHasOneAndHasManyAsVariables());
3504
    }
3505
3506
    /**
3507
     * returns itself wtih more data added as variables.
3508
     * We add has_one and has_many as variables like this: $this->MyHasOne_serialized = serialize($this->MyHasOne()).
3509
     *
3510
     * @return Order - with most important has one and has many items included as variables.
3511
     **/
3512
    protected function addHasOneAndHasManyAsVariables()
3513
    {
3514
        $object = clone $this;
3515
        $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...
3516
        $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...
3517
        $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...
3518
        $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...
3519
        $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...
3520
        $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...
3521
        $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...
3522
3523
        return $object;
3524
    }
3525
3526
/*******************************************************
3527
   * 9. TEMPLATE RELATED STUFF
3528
*******************************************************/
3529
3530
    /**
3531
     * returns the instance of EcommerceConfigAjax for use in templates.
3532
     * In templates, it is used like this:
3533
     * $EcommerceConfigAjax.TableID.
3534
     *
3535
     * @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...
3536
     **/
3537
    public function AJAXDefinitions()
3538
    {
3539
        return EcommerceConfigAjax::get_one($this);
3540
    }
3541
3542
    /**
3543
     * returns the instance of EcommerceDBConfig.
3544
     *
3545
     * @return EcommerceDBConfig
3546
     **/
3547
    public function EcomConfig()
3548
    {
3549
        return EcommerceDBConfig::current_ecommerce_db_config();
3550
    }
3551
3552
    /**
3553
     * Collects the JSON data for an ajax return of the cart.
3554
     *
3555
     * @param array $js
3556
     *
3557
     * @return array (for use in AJAX for JSON)
3558
     **/
3559
    public function updateForAjax(array $js)
3560
    {
3561
        $function = EcommerceConfig::get('Order', 'ajax_subtotal_format');
3562
        if (is_array($function)) {
3563
            list($function, $format) = $function;
3564
        }
3565
        $subTotal = $this->$function();
3566
        if (isset($format)) {
3567
            $subTotal = $subTotal->$format();
3568
            unset($format);
3569
        }
3570
        $function = EcommerceConfig::get('Order', 'ajax_total_format');
3571
        if (is_array($function)) {
3572
            list($function, $format) = $function;
3573
        }
3574
        $total = $this->$function();
3575
        if (isset($format)) {
3576
            $total = $total->$format();
3577
        }
3578
        $ajaxObject = $this->AJAXDefinitions();
3579
        $js[] = array(
3580
            't' => 'id',
3581
            's' => $ajaxObject->TableSubTotalID(),
3582
            'p' => 'innerHTML',
3583
            'v' => $subTotal,
3584
        );
3585
        $js[] = array(
3586
            't' => 'id',
3587
            's' => $ajaxObject->TableTotalID(),
3588
            'p' => 'innerHTML',
3589
            'v' => $total,
3590
        );
3591
        $js[] = array(
3592
            't' => 'class',
3593
            's' => $ajaxObject->TotalItemsClassName(),
3594
            'p' => 'innerHTML',
3595
            'v' => $this->TotalItems($recalculate = true),
3596
        );
3597
        $js[] = array(
3598
            't' => 'class',
3599
            's' => $ajaxObject->TotalItemsTimesQuantityClassName(),
3600
            'p' => 'innerHTML',
3601
            'v' => $this->TotalItemsTimesQuantity(),
3602
        );
3603
        $js[] = array(
3604
            't' => 'class',
3605
            's' => $ajaxObject->ExpectedCountryClassName(),
3606
            'p' => 'innerHTML',
3607
            'v' => $this->ExpectedCountryName(),
3608
        );
3609
3610
        return $js;
3611
    }
3612
3613
    /**
3614
     * @ToDO: move to more appropriate class
3615
     *
3616
     * @return float
3617
     **/
3618
    public function SubTotalCartValue()
3619
    {
3620
        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...
3621
    }
3622
3623
/*******************************************************
3624
   * 10. STANDARD SS METHODS (requireDefaultRecords, onBeforeDelete, etc...)
3625
*******************************************************/
3626
3627
    /**
3628
     *standard SS method.
3629
     **/
3630
    public function populateDefaults()
3631
    {
3632
        parent::populateDefaults();
3633
    }
3634
3635
    public function onBeforeWrite()
3636
    {
3637
        parent::onBeforeWrite();
3638
        if (! $this->getCanHaveShippingAddress()) {
3639
            $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...
3640
        }
3641
        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...
3642
            $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...
3643
        }
3644
        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...
3645
            $generator = Injector::inst()->create('RandomGenerator');
3646
            $token = $generator->randomToken('sha1');
3647
            $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...
3648
        }
3649
    }
3650
3651
    /**
3652
     * standard SS method
3653
     * adds the ability to update order after writing it.
3654
     **/
3655
    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...
3656
    {
3657
        parent::onAfterWrite();
3658
        //crucial!
3659
        self::set_needs_recalculating(true, $this->ID);
3660
        // quick double-check
3661
        if ($this->IsCancelled() && ! $this->IsArchived()) {
3662
            $this->Archive($avoidWrites = true);
3663
        }
3664
        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...
3665
            //do nothing
3666
        } else {
3667
            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...
3668
                $this->calculateOrderAttributes($recalculate = false);
3669
                if (EcommerceRole::current_member_is_shop_admin()) {
3670
                    if (isset($_REQUEST['SubmitOrderViaCMS'])) {
3671
                        $this->tryToFinaliseOrder();
3672
                        //just in case it writes again...
3673
                        unset($_REQUEST['SubmitOrderViaCMS']);
3674
                    }
3675
                }
3676
            }
3677
        }
3678
    }
3679
3680
    /**
3681
     *standard SS method.
3682
     *
3683
     * delete attributes, statuslogs, and payments
3684
     * THIS SHOULD NOT BE USED AS ORDERS SHOULD BE CANCELLED NOT DELETED
3685
     */
3686
    public function onBeforeDelete()
3687
    {
3688
        parent::onBeforeDelete();
3689
        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...
3690
            foreach ($attributes as $attribute) {
3691
                $attribute->delete();
3692
                $attribute->destroy();
3693
            }
3694
        }
3695
3696
        //THE REST WAS GIVING ERRORS - POSSIBLY DUE TO THE FUNNY RELATIONSHIP (one-one, two times...)
3697
        /*
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...
3698
        if($billingAddress = $this->BillingAddress()) {
3699
            if($billingAddress->exists()) {
3700
                $billingAddress->delete();
3701
                $billingAddress->destroy();
3702
            }
3703
        }
3704
        if($shippingAddress = $this->ShippingAddress()) {
3705
            if($shippingAddress->exists()) {
3706
                $shippingAddress->delete();
3707
                $shippingAddress->destroy();
3708
            }
3709
        }
3710
3711
        if($statuslogs = $this->OrderStatusLogs()){
3712
            foreach($statuslogs as $log){
3713
                $log->delete();
3714
                $log->destroy();
3715
            }
3716
        }
3717
        if($payments = $this->Payments()){
3718
            foreach($payments as $payment){
3719
                $payment->delete();
3720
                $payment->destroy();
3721
            }
3722
        }
3723
        if($emails = $this->Emails()) {
3724
            foreach($emails as $email){
3725
                $email->delete();
3726
                $email->destroy();
3727
            }
3728
        }
3729
        */
3730
    }
3731
3732
/*******************************************************
3733
   * 11. DEBUG
3734
*******************************************************/
3735
3736
    /**
3737
     * Debug helper method.
3738
     * Can be called from /shoppingcart/debug/.
3739
     *
3740
     * @return string
3741
     */
3742
    public function debug()
3743
    {
3744
        $this->calculateOrderAttributes(true);
3745
3746
        return EcommerceTaskDebugCart::debug_object($this);
3747
    }
3748
}
3749