Completed
Push — master ( 1552c3...bf9266 )
by Nicolaas
15:17
created

Order::IsSeparateShippingAddress()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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

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

namespace YourVendor;

class YourClass { }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading history...
476
     */
477
    public function CMSEditLink($action = null)
478
    {
479
        return CMSEditLinkAPI::find_edit_link_for_object($this, $action, 'sales-advanced');
480
    }
481
482
    /**
483
     * STANDARD SILVERSTRIPE STUFF
484
     * broken up into submitted and not (yet) submitted.
485
     **/
486
    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...
487
    {
488
        $fields = $this->scaffoldFormFields(array(
489
            // Don't allow has_many/many_many relationship editing before the record is first saved
490
            'includeRelations' => false,
491
            'tabbed' => true,
492
            'ajaxSafe' => true
493
        ));
494
        $fields->insertBefore(
495
            Tab::create(
496
                'Next',
497
                _t('Order.NEXT_TAB', 'Action')
498
            ),
499
            '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...
500
        );
501
        $fields->addFieldsToTab(
502
            'Root',
503
            array(
504
                Tab::create(
505
                    "Items",
506
                    _t('Order.ITEMS_TAB', 'Items')
507
                ),
508
                Tab::create(
509
                    "Extras",
510
                    _t('Order.MODIFIERS_TAB', 'Adjustments')
511
                ),
512
                Tab::create(
513
                    'Emails',
514
                    _t('Order.EMAILS_TAB', 'Emails')
515
                ),
516
                Tab::create(
517
                    'Payments',
518
                    _t('Order.PAYMENTS_TAB', 'Payment')
519
                ),
520
                Tab::create(
521
                    'Account',
522
                    _t('Order.ACCOUNT_TAB', 'Account')
523
                ),
524
                Tab::create(
525
                    'Currency',
526
                    _t('Order.CURRENCY_TAB', 'Currency')
527
                ),
528
                Tab::create(
529
                    'Addresses',
530
                    _t('Order.ADDRESSES_TAB', 'Addresses')
531
                ),
532
                Tab::create(
533
                    'Log',
534
                    _t('Order.LOG_TAB', 'Notes')
535
                ),
536
                Tab::create(
537
                    'Cancellations',
538
                    _t('Order.CANCELLATION_TAB', 'Cancel')
539
                ),
540
            )
541
        );
542
        //as we are no longer using the parent:;getCMSFields
543
        // we had to add the updateCMSFields hook.
544
        $this->extend('updateCMSFields', $fields);
545
        $currentMember = Member::currentUser();
546
        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...
547
            $firstStep = DataObject::get_one('OrderStep');
548
            $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...
549
            $this->write();
550
        }
551
        $submitted = $this->IsSubmitted() ? true : false;
552
        if ($submitted) {
553
            //TODO
554
            //Having trouble here, as when you submit the form (for example, a payment confirmation)
555
            //as the step moves forward, meaning the fields generated are incorrect, causing an error
556
            //"I can't handle sub-URLs of a Form object." generated by the RequestHandler.
557
            //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
558
            //Or something similar.
559
            //why not check if the URL == $this->CMSEditLink()
560
            //and only tryToFinaliseOrder if this is true....
561
            if ($_SERVER['REQUEST_URI'] == $this->CMSEditLink() || $_SERVER['REQUEST_URI'] == $this->CMSEditLink('edit')) {
562
                $this->tryToFinaliseOrder();
563
            }
564
        } else {
565
            $this->init(true);
566
            $this->calculateOrderAttributes(true);
567
            Session::set('EcommerceOrderGETCMSHack', $this->ID);
568
        }
569
        if ($submitted) {
570
            $this->fieldsAndTabsToBeRemoved[] = 'CustomerOrderNote';
571
        } else {
572
            $this->fieldsAndTabsToBeRemoved[] = 'Emails';
573
        }
574
        foreach ($this->fieldsAndTabsToBeRemoved as $field) {
575
            $fields->removeByName($field);
576
        }
577
        $orderSummaryConfig = GridFieldConfig_Base::create();
578
        $orderSummaryConfig->removeComponentsByType('GridFieldToolbarHeader');
579
        // $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...
580
        $orderSummaryConfig->removeComponentsByType('GridFieldFilterHeader');
581
        $orderSummaryConfig->removeComponentsByType('GridFieldPageCount');
582
        $orderSummaryConfig->removeComponentsByType('GridFieldPaginator');
583
        $nextFieldArray = array(
584
            LiteralField::create('CssFix', '<style>#Root_Next h2.form-control {padding: 0!important; margin: 0!important; padding-top: 4em!important;}</style>'),
585
            HeaderField::create('MyOrderStepHeader', _t('Order.CURRENT_STATUS', '1. Current Status')),
586
            $this->OrderStepField(),
587
            GridField::create(
588
                'OrderSummary',
589
                _t('Order.CURRENT_STATUS', 'Summary'),
590
                ArrayList::create(array($this)),
591
                $orderSummaryConfig
592
            )
593
        );
594
        $keyNotes = OrderStatusLog::get()->filter(
595
            array(
596
                'OrderID' => $this->ID,
597
                'ClassName' => 'OrderStatusLog'
598
            )
599
        );
600
        if ($keyNotes->count()) {
601
            $notesSummaryConfig = GridFieldConfig_RecordViewer::create();
602
            $notesSummaryConfig->removeComponentsByType('GridFieldToolbarHeader');
603
            $notesSummaryConfig->removeComponentsByType('GridFieldFilterHeader');
604
            // $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...
605
            $notesSummaryConfig->removeComponentsByType('GridFieldPageCount');
606
            $notesSummaryConfig->removeComponentsByType('GridFieldPaginator');
607
            $nextFieldArray = array_merge(
608
                $nextFieldArray,
609
                array(
610
                    HeaderField::create('KeyNotesHeader', _t('Order.KEY_NOTES_HEADER', 'Key Notes')),
611
                    GridField::create(
612
                        'OrderStatusLogSummary',
613
                        _t('Order.CURRENT_KEY_NOTES', 'Key Notes'),
614
                        $keyNotes,
615
                        $notesSummaryConfig
616
                    )
617
                )
618
            );
619
        }
620
        $nextFieldArray = array_merge(
621
            $nextFieldArray,
622
            array(
623
                EcommerceCMSButtonField::create(
624
                    'AddNoteButton',
625
                    '/admin/sales/Order/EditForm/field/Order/item/' . $this->ID . '/ItemEditForm/field/OrderStatusLog/item/new',
626
                    _t('Order.ADD_NOTE', 'Add Note')
627
                )
628
            )
629
        );
630
        $nextFieldArray = array_merge(
631
            $nextFieldArray,
632
            array(
633
634
            )
635
        );
636
637
         //is the member is a shop admin they can always view it
638
639
        if (EcommerceRole::current_member_can_process_orders(Member::currentUser())) {
640
            $lastStep = OrderStep::last_order_step();
641
            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...
642
                $queueObjectSingleton = Injector::inst()->get('OrderProcessQueue');
643
                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...
644
                    $myQueueObjectField = GridField::create(
645
                        'MyQueueObjectField',
646
                        _t('Order.QUEUE_DETAILS', 'Queue Details'),
647
                        $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...
648
                        GridFieldConfig_RecordEditor::create()
649
                    );
650
                } else {
651
                    $myQueueObjectField = LiteralField::create('MyQueueObjectField', '<p>'._t('Order.NOT_QUEUED','This order is not queued for future processing.').'</p>');
652
                }
653
                $nextFieldArray = array_merge(
654
                    $nextFieldArray,
655
                    array(
656
                        HeaderField::create('OrderStepNextStepHeader', _t('Order.ACTION_NEXT_STEP', '2. Action Next Step')),
657
                        $myQueueObjectField,
658
                        HeaderField::create('ActionNextStepManually', _t('Order.MANUAL_STATUS_CHANGE', '3. Move Order Along')),
659
                        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>'),
660
                        EcommerceCMSButtonField::create(
661
                            'StatusIDExplanation',
662
                            $this->CMSEditLink(),
663
                            _t('Order.REFRESH', 'refresh now')
664
                        )
665
                    )
666
                );
667
            }
668
        }
669
        $fields->addFieldsToTab(
670
            'Root.Next',
671
            $nextFieldArray
672
        );
673
674
        $this->MyStep()->addOrderStepFields($fields, $this);
675
676
        if ($submitted) {
677
            $permaLinkLabel = _t('Order.PERMANENT_LINK', 'Customer Link');
678
            $html = '<p>'.$permaLinkLabel.': <a href="'.$this->getRetrieveLink().'">'.$this->getRetrieveLink().'</a></p>';
679
            $shareLinkLabel = _t('Order.SHARE_LINK', 'Share Link');
680
            $html .= '<p>'.$shareLinkLabel.': <a href="'.$this->getShareLink().'">'.$this->getShareLink().'</a></p>';
681
            $feedbackLinkLabel = _t('Order.FEEDBACK_LINK', 'Feedback Link');
682
            $html .= '<p>'.$feedbackLinkLabel.': <a href="'.$this->getFeedbackLink().'">'.$this->getFeedbackLink().'</a></p>';
683
            $js = "window.open(this.href, 'payment', 'toolbar=0,scrollbars=1,location=1,statusbar=1,menubar=0,resizable=1,width=800,height=600'); return false;";
684
            $link = $this->getPrintLink();
685
            $label = _t('Order.PRINT_INVOICE', 'invoice');
686
            $linkHTML = '<a href="'.$link.'" onclick="'.$js.'">'.$label.'</a>';
687
            $linkHTML .= ' | ';
688
            $link = $this->getPackingSlipLink();
689
            $label = _t('Order.PRINT_PACKING_SLIP', 'packing slip');
690
            $labelPrint = _t('Order.PRINT', 'Print');
691
            $linkHTML .= '<a href="'.$link.'" onclick="'.$js.'">'.$label.'</a>';
692
            $html .= '<h3>';
693
            $html .= $labelPrint.': '.$linkHTML;
694
            $html .= '</h3>';
695
696
            $fields->addFieldToTab(
697
                'Root.Main',
698
                LiteralField::create('getPrintLinkANDgetPackingSlipLink', $html)
699
            );
700
701
            //add order here as well.
702
            $fields->addFieldToTab(
703
                'Root.Main',
704
                new LiteralField(
705
                    'MainDetails',
706
                    '<iframe src="'.$this->getPrintLink().'" width="100%" height="2500" style="border: 5px solid #2e7ead; border-radius: 2px;"></iframe>')
707
            );
708
            $fields->addFieldsToTab(
709
                'Root.Items',
710
                array(
711
                    GridField::create(
712
                        'Items_Sold',
713
                        'Items Sold',
714
                        $this->Items(),
715
                        new GridFieldConfig_RecordViewer
716
                    )
717
                )
718
            );
719
            $fields->addFieldsToTab(
720
                'Root.Extras',
721
                array(
722
                    GridField::create(
723
                        'Modifications',
724
                        'Price (and other) adjustments',
725
                        $this->Modifiers(),
726
                        new GridFieldConfig_RecordViewer
727
                    )
728
                )
729
            );
730
            $fields->addFieldsToTab(
731
                'Root.Emails',
732
                array(
733
                    $this->getEmailsTableField()
734
                )
735
            );
736
            $fields->addFieldsToTab(
737
                'Root.Payments',
738
                array(
739
                    $this->getPaymentsField(),
740
                    new ReadOnlyField('TotalPaidNice', _t('Order.TOTALPAID', 'Total Paid'), $this->TotalPaidAsCurrencyObject()->Nice()),
741
                    new ReadOnlyField('TotalOutstandingNice', _t('Order.TOTALOUTSTANDING', 'Total Outstanding'), $this->getTotalOutstandingAsMoney()->Nice())
742
                )
743
            );
744
            if ($this->canPay()) {
745
                $link = EcommercePaymentController::make_payment_link($this->ID);
746
                $js = "window.open(this.href, 'payment', 'toolbar=0,scrollbars=1,location=1,statusbar=1,menubar=0,resizable=1,width=800,height=600'); return false;";
747
                $header = _t('Order.MAKEPAYMENT', 'make payment');
748
                $label = _t('Order.MAKEADDITIONALPAYMENTNOW', 'make additional payment now');
749
                $linkHTML = '<a href="'.$link.'" onclick="'.$js.'">'.$label.'</a>';
750
                $fields->addFieldToTab('Root.Payments', new HeaderField('MakeAdditionalPaymentHeader', $header, 3));
751
                $fields->addFieldToTab('Root.Payments', new LiteralField('MakeAdditionalPayment', $linkHTML));
752
            }
753
            //member
754
            $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...
755
            if ($member && $member->exists()) {
756
                $fields->addFieldToTab('Root.Account', new LiteralField('MemberDetails', $member->getEcommerceFieldsForCMS()));
757
            } else {
758
                $fields->addFieldToTab('Root.Account', new LiteralField('MemberDetails',
759
                    '<p>'._t('Order.NO_ACCOUNT', 'There is no --- account --- associated with this order').'</p>'
760
                ));
761
            }
762
            if($this->getFeedbackLink()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->getFeedbackLink() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

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

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

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

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

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

Loading history...
1245
            $this->extend('doCancel', $member, $log);
1246
1247
            return $log;
1248
        }
1249
    }
1250
1251
    /**
1252
     * returns true if successful.
1253
     *
1254
     * @param bool $avoidWrites
1255
     *
1256
     * @return bool
1257
     */
1258
    public function Archive($avoidWrites = true)
1259
    {
1260
        $lastOrderStep = OrderStep::last_order_step();
1261
        if ($lastOrderStep) {
1262
            if ($avoidWrites) {
1263
                DB::query('
1264
                    UPDATE "Order"
1265
                    SET "Order"."StatusID" = '.$lastOrderStep->ID.'
1266
                    WHERE "Order"."ID" = '.$this->ID.'
1267
                    LIMIT 1
1268
                ');
1269
1270
                return true;
1271
            } else {
1272
                $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...
1273
                $this->write();
1274
1275
                return true;
1276
            }
1277
        }
1278
1279
        return false;
1280
    }
1281
1282
/*******************************************************
1283
   * 3. STATUS RELATED FUNCTIONS / SHORTCUTS
1284
*******************************************************/
1285
1286
    /**
1287
     * Avoids caching of $this->Status().
1288
     *
1289
     * @return DataObject (current OrderStep)
1290
     */
1291
    public function MyStep()
1292
    {
1293
        $step = null;
1294
        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...
1295
            $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...
1296
        }
1297
        if (! $step) {
1298
            $step = DataObject::get_one(
1299
                'OrderStep',
1300
                null,
1301
                $cacheDataObjectGetOne = false
1302
            );
1303
        }
1304
        if (! $step) {
1305
            $step = OrderStep_Created::create();
1306
        }
1307
        if (! $step) {
1308
            user_error('You need an order step in your Database.');
1309
        }
1310
1311
        return $step;
1312
    }
1313
1314
    /**
1315
     * Return the OrderStatusLog that is relevant to the Order status.
1316
     *
1317
     * @return OrderStatusLog
1318
     */
1319
    public function RelevantLogEntry()
1320
    {
1321
        return $this->MyStep()->RelevantLogEntry($this);
1322
    }
1323
1324
    /**
1325
     * @return OrderStep (current OrderStep that can be seen by customer)
1326
     */
1327
    public function CurrentStepVisibleToCustomer()
1328
    {
1329
        $obj = $this->MyStep();
1330
        if ($obj->HideStepFromCustomer) {
1331
            $obj = OrderStep::get()->where('"OrderStep"."Sort" < '.$obj->Sort.' AND "HideStepFromCustomer" = 0')->Last();
1332
            if (!$obj) {
1333
                $obj = DataObject::get_one('OrderStep');
1334
            }
1335
        }
1336
1337
        return $obj;
1338
    }
1339
1340
    /**
1341
     * works out if the order is still at the first OrderStep.
1342
     *
1343
     * @return bool
1344
     */
1345
    public function IsFirstStep()
1346
    {
1347
        $firstStep = DataObject::get_one('OrderStep');
1348
        $currentStep = $this->MyStep();
1349
        if ($firstStep && $currentStep) {
1350
            if ($firstStep->ID == $currentStep->ID) {
1351
                return true;
1352
            }
1353
        }
1354
1355
        return false;
1356
    }
1357
1358
    /**
1359
     * Is the order still being "edited" by the customer?
1360
     *
1361
     * @return bool
1362
     */
1363
    public function IsInCart()
1364
    {
1365
        return (bool) $this->IsSubmitted() ? false : true;
1366
    }
1367
1368
    /**
1369
     * The order has "passed" the IsInCart phase.
1370
     *
1371
     * @return bool
1372
     */
1373
    public function IsPastCart()
1374
    {
1375
        return (bool) $this->IsInCart() ? false : true;
1376
    }
1377
1378
    /**
1379
     * Are there still steps the order needs to go through?
1380
     *
1381
     * @return bool
1382
     */
1383
    public function IsUncomplete()
1384
    {
1385
        return (bool) $this->MyStep()->ShowAsUncompletedOrder;
1386
    }
1387
1388
    /**
1389
     * Is the order in the :"processing" phaase.?
1390
     *
1391
     * @return bool
1392
     */
1393
    public function IsProcessing()
1394
    {
1395
        return (bool) $this->MyStep()->ShowAsInProcessOrder;
1396
    }
1397
1398
    /**
1399
     * Is the order completed?
1400
     *
1401
     * @return bool
1402
     */
1403
    public function IsCompleted()
1404
    {
1405
        return (bool) $this->MyStep()->ShowAsCompletedOrder;
1406
    }
1407
1408
    /**
1409
     * Has the order been paid?
1410
     * TODO: why do we check if there is a total at all?
1411
     *
1412
     * @return bool
1413
     */
1414
    public function IsPaid()
1415
    {
1416
        if ($this->IsSubmitted()) {
1417
            return (bool) (($this->Total() >= 0) && ($this->TotalOutstanding() <= 0));
1418
        }
1419
1420
        return false;
1421
    }
1422
1423
    /**
1424
     * @alias for getIsPaidNice
1425
     * @return string
1426
     */
1427
    public function IsPaidNice()
1428
    {
1429
        return $this->getIsPaidNice();
1430
    }
1431
1432
1433
    public function getIsPaidNice()
1434
    {
1435
        return $this->IsPaid() ? 'yes' : 'no';
1436
    }
1437
1438
1439
    /**
1440
     * Has the order been paid?
1441
     * TODO: why do we check if there is a total at all?
1442
     *
1443
     * @return bool
1444
     */
1445
    public function PaymentIsPending()
1446
    {
1447
        if ($this->IsSubmitted()) {
1448
            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...
1449
                //do nothing;
1450
            } 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...
1451
                foreach ($payments as $payment) {
1452
                    if ('Pending' == $payment->Status) {
1453
                        return true;
1454
                    }
1455
                }
1456
            }
1457
        }
1458
1459
        return false;
1460
    }
1461
1462
    /**
1463
     * shows payments that are meaningfull
1464
     * if the order has been paid then only show successful payments.
1465
     *
1466
     * @return DataList
1467
     */
1468
    public function RelevantPayments()
1469
    {
1470
        if ($this->IsPaid()) {
1471
            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...
1472
            //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...
1473
            //	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...
1474
        } else {
1475
            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...
1476
        }
1477
    }
1478
1479
1480
    /**
1481
     * Has the order been cancelled?
1482
     *
1483
     * @return bool
1484
     */
1485
    public function IsCancelled()
1486
    {
1487
        return $this->getIsCancelled();
1488
    }
1489
    public function getIsCancelled()
1490
    {
1491
        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...
1492
    }
1493
1494
    /**
1495
     * Has the order been cancelled by the customer?
1496
     *
1497
     * @return bool
1498
     */
1499
    public function IsCustomerCancelled()
1500
    {
1501
        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...
1502
            return true;
1503
        }
1504
1505
        return false;
1506
    }
1507
1508
    /**
1509
     * Has the order been cancelled by the  administrator?
1510
     *
1511
     * @return bool
1512
     */
1513
    public function IsAdminCancelled()
1514
    {
1515
        if ($this->IsCancelled()) {
1516
            if (!$this->IsCustomerCancelled()) {
1517
                $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...
1518
                if ($admin) {
1519
                    if ($admin->IsShopAdmin()) {
1520
                        return true;
1521
                    }
1522
                }
1523
            }
1524
        }
1525
1526
        return false;
1527
    }
1528
1529
    /**
1530
     * Is the Shop Closed for business?
1531
     *
1532
     * @return bool
1533
     */
1534
    public function ShopClosed()
1535
    {
1536
        return EcomConfig()->ShopClosed;
1537
    }
1538
1539
/*******************************************************
1540
   * 4. LINKING ORDER WITH MEMBER AND ADDRESS
1541
*******************************************************/
1542
1543
    /**
1544
     * Returns a member linked to the order.
1545
     * If a member is already linked, it will return the existing member.
1546
     * Otherwise it will return a new Member.
1547
     *
1548
     * Any new member is NOT written, because we dont want to create a new member unless we have to!
1549
     * We will not add a member to the order unless a new one is created in the checkout
1550
     * OR the member is logged in / logs in.
1551
     *
1552
     * Also note that if a new member is created, it is not automatically written
1553
     *
1554
     * @param bool $forceCreation - if set to true then the member will always be saved in the database.
1555
     *
1556
     * @return Member
1557
     **/
1558
    public function CreateOrReturnExistingMember($forceCreation = false)
1559
    {
1560
        if ($this->IsSubmitted()) {
1561
            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...
1562
        }
1563
        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...
1564
            $member = $this->Member();
0 ignored issues
show
Bug introduced by
The method Member() does not exist on Order. Did you maybe mean CreateOrReturnExistingMember()?

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

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

Loading history...
Unused Code introduced by
$member is not used, you could remove the assignment.

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

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

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

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

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

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

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

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2234
        if ($extended !== null) {
2235
            return $extended;
2236
        }
2237
        if ($member->exists()) {
2238
            return $member->IsShopAdmin();
2239
        }
2240
    }
2241
2242
    /**
2243
     * Standard SS method - can the current member view this order?
2244
     *
2245
     * @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...
2246
     *
2247
     * @return bool
2248
     **/
2249
    public function canView($member = null)
2250
    {
2251
        if (!$this->exists()) {
2252
            return true;
2253
        }
2254
        $member = $this->getMemberForCanFunctions($member);
2255
        //check if this has been "altered" in any DataExtension
2256
        $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...
2257
        if ($extended !== null) {
2258
            return $extended;
2259
        }
2260
        //is the member is a shop admin they can always view it
2261
        if (EcommerceRole::current_member_is_shop_admin($member)) {
2262
            return true;
2263
        }
2264
2265
        //is the member is a shop assistant they can always view it
2266
        if (EcommerceRole::current_member_is_shop_assistant($member)) {
2267
            return true;
2268
        }
2269
        //if the current member OWNS the order, (s)he can always view it.
2270
        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...
2271
            return true;
2272
        }
2273
        //it is the current order
2274
        if ($this->IsInSession()) {
2275
            //we do some additional CHECKS for session hackings!
2276
            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...
2277
                //can't view the order of another member!
2278
                //shop admin exemption is already captured.
2279
                //this is always true
2280
                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...
2281
                    return false;
2282
                }
2283
            } else {
2284
                //order belongs to someone, but current user is NOT logged in...
2285
                //this is allowed!
2286
                //the reason it is allowed is because we want to be able to
2287
                //add order to non-existing member
2288
                return true;
2289
            }
2290
        }
2291
2292
        return false;
2293
    }
2294
2295
    /**
2296
     * @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...
2297
     * @return bool
2298
     */
2299
    public function canOverrideCanView($member = null)
2300
    {
2301
        if ($this->canView($member)) {
2302
            //can view overrides any concerns
2303
            return true;
2304
        } else {
2305
            $tsOrder = strtotime($this->LastEdited);
2306
            $tsNow = time();
2307
            $minutes = EcommerceConfig::get('Order', 'minutes_an_order_can_be_viewed_without_logging_in');
2308
            if ($minutes && ((($tsNow - $tsOrder) / 60) < $minutes)) {
2309
2310
                //has the order been edited recently?
2311
                return true;
2312
            } elseif ($orderStep = $this->MyStep()) {
2313
2314
                // order is being processed ...
2315
                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...
2316
            }
2317
        }
2318
        return false;
2319
    }
2320
2321
    /**
2322
     * @return bool
2323
     */
2324
    public function IsInSession()
2325
    {
2326
        $orderInSession = ShoppingCart::session_order();
2327
2328
        return $orderInSession && $this->ID && $this->ID == $orderInSession->ID;
2329
    }
2330
2331
    /**
2332
     * returns a pseudo random part of the session id.
2333
     *
2334
     * @param int $size
2335
     *
2336
     * @return string
2337
     */
2338
    public function LessSecureSessionID($size = 7, $start = null)
2339
    {
2340
        if (!$start || $start < 0 || $start > (32 - $size)) {
2341
            $start = 0;
2342
        }
2343
2344
        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...
2345
    }
2346
    /**
2347
     *
2348
     * @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...
2349
     *
2350
     * @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...
2351
     **/
2352
    public function canViewAdminStuff($member = null)
2353
    {
2354
        $member = $this->getMemberForCanFunctions($member);
2355
        $extended = $this->extendedCan(__FUNCTION__, $member);
0 ignored issues
show
Documentation introduced by
$member is of type object<DataObject>, but the function expects a object<Member>|integer.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2356
        if ($extended !== null) {
2357
            return $extended;
2358
        }
2359
        if (Permission::checkMember($member, Config::inst()->get('EcommerceRole', 'admin_permission_code'))) {
2360
            return true;
2361
        }
2362
    }
2363
2364
    /**
2365
     * if we set canEdit to false then we
2366
     * can not see the child records
2367
     * Basically, you can edit when you can view and canEdit (even as a customer)
2368
     * Or if you are a Shop Admin you can always edit.
2369
     * Otherwise it is false...
2370
     *
2371
     * @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...
2372
     *
2373
     * @return bool
2374
     **/
2375
    public function canEdit($member = null)
2376
    {
2377
        $member = $this->getMemberForCanFunctions($member);
2378
        $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...
2379
        if ($extended !== null) {
2380
            return $extended;
2381
        }
2382
        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...
2383
            return true;
2384
        }
2385
        if (Permission::checkMember($member, Config::inst()->get('EcommerceRole', 'admin_permission_code'))) {
2386
            return true;
2387
        }
2388
        //is the member is a shop assistant they can always view it
2389
        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...
2390
            return true;
2391
        }
2392
        return false;
2393
    }
2394
2395
    /**
2396
     * is the order ready to go through to the
2397
     * checkout process.
2398
     *
2399
     * This method checks all the order items and order modifiers
2400
     * If any of them need immediate attention then this is done
2401
     * first after which it will go through to the checkout page.
2402
     *
2403
     * @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...
2404
     *
2405
     * @return bool
2406
     **/
2407
    public function canCheckout(Member $member = null)
2408
    {
2409
        $member = $this->getMemberForCanFunctions($member);
2410
        $extended = $this->extendedCan(__FUNCTION__, $member);
0 ignored issues
show
Documentation introduced by
$member is of type object<DataObject>, but the function expects a object<Member>|integer.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2411
        if ($extended !== null) {
2412
            return $extended;
2413
        }
2414
        $submitErrors = $this->SubmitErrors();
2415
        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...
2416
            return false;
2417
        }
2418
2419
        return true;
2420
    }
2421
2422
    /**
2423
     * Can the order be submitted?
2424
     * this method can be used to stop an order from being submitted
2425
     * due to something not being completed or done.
2426
     *
2427
     * @see Order::SubmitErrors
2428
     *
2429
     * @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...
2430
     *
2431
     * @return bool
2432
     **/
2433
    public function canSubmit(Member $member = null)
2434
    {
2435
        $member = $this->getMemberForCanFunctions($member);
2436
        $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...
2437
        if ($extended !== null) {
2438
            return $extended;
2439
        }
2440
        if ($this->IsSubmitted()) {
2441
            return false;
2442
        }
2443
        $submitErrors = $this->SubmitErrors();
2444
        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...
2445
            return false;
2446
        }
2447
2448
        return true;
2449
    }
2450
2451
    /**
2452
     * Can a payment be made for this Order?
2453
     *
2454
     * @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...
2455
     *
2456
     * @return bool
2457
     **/
2458
    public function canPay(Member $member = null)
2459
    {
2460
        $member = $this->getMemberForCanFunctions($member);
2461
        $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...
2462
        if ($extended !== null) {
2463
            return $extended;
2464
        }
2465
        if ($this->IsPaid() || $this->IsCancelled() || $this->PaymentIsPending()) {
2466
            return false;
2467
        }
2468
2469
        return $this->MyStep()->CustomerCanPay;
2470
    }
2471
2472
    /**
2473
     * Can the given member cancel this order?
2474
     *
2475
     * @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...
2476
     *
2477
     * @return bool
2478
     **/
2479
    public function canCancel(Member $member = null)
2480
    {
2481
        //if it is already cancelled it can not be cancelled again
2482
        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...
2483
            return false;
2484
        }
2485
        $member = $this->getMemberForCanFunctions($member);
2486
        $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...
2487
        if ($extended !== null) {
2488
            return $extended;
2489
        }
2490
        if (EcommerceRole::current_member_can_process_orders($member)) {
2491
            return true;
2492
        }
2493
2494
        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...
2495
    }
2496
2497
    /**
2498
     * @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...
2499
     *
2500
     * @return bool
2501
     **/
2502
    public function canDelete($member = null)
2503
    {
2504
        $member = $this->getMemberForCanFunctions($member);
2505
        $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...
2506
        if ($extended !== null) {
2507
            return $extended;
2508
        }
2509
        if ($this->IsSubmitted()) {
2510
            return false;
2511
        }
2512
        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...
2513
            return true;
2514
        }
2515
2516
        return false;
2517
    }
2518
2519
    /**
2520
     * Returns all the order logs that the current member can view
2521
     * i.e. some order logs can only be viewed by the admin (e.g. suspected fraud orderlog).
2522
     *
2523
     * @return ArrayList (OrderStatusLogs)
2524
     **/
2525
    public function CanViewOrderStatusLogs()
2526
    {
2527
        $canViewOrderStatusLogs = new ArrayList();
2528
        $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...
2529
        foreach ($logs as $log) {
2530
            if ($log->canView()) {
2531
                $canViewOrderStatusLogs->push($log);
2532
            }
2533
        }
2534
2535
        return $canViewOrderStatusLogs;
2536
    }
2537
2538
    /**
2539
     * returns all the logs that can be viewed by the customer.
2540
     *
2541
     * @return ArrayList (OrderStausLogs)
2542
     */
2543
    public function CustomerViewableOrderStatusLogs()
2544
    {
2545
        $customerViewableOrderStatusLogs = new ArrayList();
2546
        $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...
2547
        if ($logs) {
2548
            foreach ($logs as $log) {
2549
                if (!$log->InternalUseOnly) {
2550
                    $customerViewableOrderStatusLogs->push($log);
2551
                }
2552
            }
2553
        }
2554
2555
        return $customerViewableOrderStatusLogs;
2556
    }
2557
2558
/*******************************************************
2559
   * 8. GET METHODS (e.g. Total, SubTotal, Title, etc...)
2560
*******************************************************/
2561
2562
    /**
2563
     * returns the email to be used for customer communication.
2564
     *
2565
     * @return string
2566
     */
2567
    public function OrderEmail()
2568
    {
2569
        return $this->getOrderEmail();
2570
    }
2571
    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...
2572
    {
2573
        $email = '';
2574
        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...
2575
            $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...
2576
        }
2577
        if (! $email) {
2578
            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...
2579
                $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...
2580
            }
2581
        }
2582
        $extendedEmail = $this->extend('updateOrderEmail', $email);
2583
        if ($extendedEmail !== null && is_array($extendedEmail) && count($extendedEmail)) {
2584
            $email = implode(';', $extendedEmail);
2585
        }
2586
2587
        return $email;
2588
    }
2589
2590
    /**
2591
     * Returns true if there is a prink or email link.
2592
     *
2593
     * @return bool
2594
     */
2595
    public function HasPrintOrEmailLink()
2596
    {
2597
        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...
2598
    }
2599
2600
    /**
2601
     * returns the absolute link to the order that can be used in the customer communication (email).
2602
     *
2603
     * @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...
2604
     */
2605
    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...
2606
    {
2607
        return $this->getEmailLink();
2608
    }
2609
    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...
2610
    {
2611
        if (!isset($_REQUEST['print'])) {
2612
            if ($this->IsSubmitted()) {
2613
                return Director::AbsoluteURL(OrderConfirmationPage::get_email_link($this->ID, $this->MyStep()->getEmailClassName(), $actuallySendEmail = true));
2614
            }
2615
        }
2616
    }
2617
2618
    /**
2619
     * returns the absolute link to the order for printing.
2620
     *
2621
     * @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...
2622
     */
2623
    public function PrintLink()
2624
    {
2625
        return $this->getPrintLink();
2626
    }
2627
    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...
2628
    {
2629
        if (!isset($_REQUEST['print'])) {
2630
            if ($this->IsSubmitted()) {
2631
                return Director::AbsoluteURL(OrderConfirmationPage::get_order_link($this->ID)).'?print=1';
2632
            }
2633
        }
2634
    }
2635
2636
    /**
2637
     * returns the absolute link to the order for printing.
2638
     *
2639
     * @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...
2640
     */
2641
    public function PackingSlipLink()
2642
    {
2643
        return $this->getPackingSlipLink();
2644
    }
2645
    public function getPackingSlipLink()
2646
    {
2647
        if ($this->IsSubmitted()) {
2648
            return Director::AbsoluteURL(OrderConfirmationPage::get_order_link($this->ID)).'?packingslip=1';
2649
        }
2650
    }
2651
2652
    /**
2653
     * returns the absolute link that the customer can use to retrieve the email WITHOUT logging in.
2654
     *
2655
     * @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...
2656
     */
2657
    public function RetrieveLink()
2658
    {
2659
        return $this->getRetrieveLink();
2660
    }
2661
2662
    public function getRetrieveLink()
2663
    {
2664
        //important to recalculate!
2665
        if ($this->IsSubmitted($recalculate = true)) {
2666
            //add session ID if not added yet...
2667
            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...
2668
                $this->write();
2669
            }
2670
2671
            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...
2672
        } else {
2673
            return Director::AbsoluteURL('/shoppingcart/loadorder/'.$this->ID.'/');
2674
        }
2675
    }
2676
2677
    public function ShareLink()
2678
    {
2679
        return $this->getShareLink();
2680
    }
2681
2682
    public function getShareLink()
2683
    {
2684
        $orderItems = $this->itemsFromDatabase();
2685
        $action = 'share';
2686
        $array = array();
2687
        foreach ($orderItems as $orderItem) {
2688
            $array[] = implode(
2689
                ',',
2690
                array(
2691
                    $orderItem->BuyableClassName,
2692
                    $orderItem->BuyableID,
2693
                    $orderItem->Quantity
2694
                )
2695
            );
2696
        }
2697
2698
        return Director::AbsoluteURL(CartPage::find_link($action.'/'.implode('-', $array)));
2699
    }
2700
2701
    /**
2702
     * @alias for getFeedbackLink
2703
     * @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...
2704
     */
2705
    public function FeedbackLink()
2706
    {
2707
        return $this->getFeedbackLink();
2708
    }
2709
2710
    /**
2711
     * @return string | null
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

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

Loading history...
2712
     */
2713
    public function getFeedbackLink()
2714
    {
2715
        $orderConfirmationPage = DataObject::get_one('OrderConfirmationPage');
2716
        if($orderConfirmationPage->IsFeedbackEnabled) {
2717
2718
            return Director::AbsoluteURL($this->getRetrieveLink()).'#OrderForm_Feedback_FeedbackForm';
0 ignored issues
show
Security Bug introduced by
It seems like $this->getRetrieveLink() targeting Order::getRetrieveLink() can also be of type false; however, Director::absoluteURL() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
2719
        }
2720
    }
2721
2722
    /**
2723
     * link to delete order.
2724
     *
2725
     * @return string
2726
     */
2727
    public function DeleteLink()
2728
    {
2729
        return $this->getDeleteLink();
2730
    }
2731
    public function getDeleteLink()
2732
    {
2733
        if ($this->canDelete()) {
2734
            return ShoppingCart_Controller::delete_order_link($this->ID);
2735
        } else {
2736
            return '';
2737
        }
2738
    }
2739
2740
    /**
2741
     * link to copy order.
2742
     *
2743
     * @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...
2744
     */
2745
    public function CopyOrderLink()
2746
    {
2747
        return $this->getCopyOrderLink();
2748
    }
2749
    public function getCopyOrderLink()
2750
    {
2751
        if ($this->canView() && $this->IsSubmitted()) {
2752
            return ShoppingCart_Controller::copy_order_link($this->ID);
2753
        } else {
2754
            return '';
2755
        }
2756
    }
2757
2758
    /**
2759
     * A "Title" for the order, which summarises the main details (date, and customer) in a string.
2760
     *
2761
     * @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...
2762
     * @param bool   $includeName - e.g. by Mr Johnson
2763
     *
2764
     * @return string
2765
     **/
2766
    public function Title($dateFormat = null, $includeName = false)
2767
    {
2768
        return $this->getTitle($dateFormat, $includeName);
2769
    }
2770
    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...
2771
    {
2772
        if ($this->exists()) {
2773
            if ($dateFormat === null) {
2774
                $dateFormat = EcommerceConfig::get('Order', 'date_format_for_title');
2775
            }
2776
            if ($includeName === null) {
2777
                $includeName = EcommerceConfig::get('Order', 'include_customer_name_in_title');
2778
            }
2779
            $title = $this->i18n_singular_name()." #".number_format($this->ID);
2780
            if ($dateFormat) {
2781
                if ($submissionLog = $this->SubmissionLog()) {
2782
                    $dateObject = $submissionLog->dbObject('Created');
2783
                    $placed = _t('Order.PLACED', 'placed');
2784
                } else {
2785
                    $dateObject = $this->dbObject('Created');
2786
                    $placed = _t('Order.STARTED', 'started');
2787
                }
2788
                $title .= ' - '.$placed.' '.$dateObject->Format($dateFormat);
2789
            }
2790
            $name = '';
2791
            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...
2792
                $name = ' - '._t('Order.CANCELLED', 'CANCELLED');
2793
            }
2794
            if ($includeName) {
2795
                $by = _t('Order.BY', 'by');
2796
                if (!$name) {
2797
                    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...
2798
                        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...
2799
                            $name = ' - '.$by.' '.$billingAddress->Prefix.' '.$billingAddress->FirstName.' '.$billingAddress->Surname;
2800
                        }
2801
                    }
2802
                }
2803
                if (!$name) {
2804
                    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...
2805
                        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...
2806
                            if ($member->exists()) {
2807
                                if ($memberName = $member->getName()) {
2808
                                    if (!trim($memberName)) {
2809
                                        $memberName = _t('Order.ANONYMOUS', 'anonymous');
2810
                                    }
2811
                                    $name = ' - '.$by.' '.$memberName;
2812
                                }
2813
                            }
2814
                        }
2815
                    }
2816
                }
2817
            }
2818
            $title .= $name;
2819
        } else {
2820
            $title = _t('Order.NEW', 'New').' '.$this->i18n_singular_name();
2821
        }
2822
        $extendedTitle = $this->extend('updateTitle', $title);
2823
        if ($extendedTitle !== null && is_array($extendedTitle) && count($extendedTitle)) {
2824
            $title = implode('; ', $extendedTitle);
2825
        }
2826
2827
        return $title;
2828
    }
2829
2830
    /**
2831
     * Returns the subtotal of the items for this order.
2832
     *
2833
     * @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...
2834
     */
2835
    public function SubTotal()
2836
    {
2837
        return $this->getSubTotal();
2838
    }
2839
    public function getSubTotal()
2840
    {
2841
        $result = 0;
2842
        $items = $this->Items();
2843
        if ($items->count()) {
2844
            foreach ($items as $item) {
2845
                if (is_a($item, Object::getCustomClass('OrderAttribute'))) {
2846
                    $result += $item->Total();
2847
                }
2848
            }
2849
        }
2850
2851
        return $result;
2852
    }
2853
2854
    /**
2855
     * @return Currency (DB Object)
2856
     **/
2857
    public function SubTotalAsCurrencyObject()
2858
    {
2859
        return DBField::create_field('Currency', $this->SubTotal());
2860
    }
2861
2862
    /**
2863
     * @return Money
2864
     **/
2865
    public function SubTotalAsMoney()
2866
    {
2867
        return $this->getSubTotalAsMoney();
2868
    }
2869
    public function getSubTotalAsMoney()
2870
    {
2871
        return EcommerceCurrency::get_money_object_from_order_currency($this->SubTotal(), $this);
2872
    }
2873
2874
    /**
2875
     * @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...
2876
     * @param bool         $stopAtExcludedModifier - when this flag is TRUE, we stop adding the modifiers when we reach an excluded modifier.
2877
     *
2878
     * @return Currency (DB Object)
2879
     **/
2880
    public function ModifiersSubTotalAsCurrencyObject($excluded = null, $stopAtExcludedModifier = false)
2881
    {
2882
        return DBField::create_field('Currency', $this->ModifiersSubTotal($excluded, $stopAtExcludedModifier));
2883
    }
2884
2885
    /**
2886
     * @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...
2887
     * @param bool         $stopAtExcludedModifier - when this flag is TRUE, we stop adding the modifiers when we reach an excluded modifier.
2888
     *
2889
     * @return Money (DB Object)
2890
     **/
2891
    public function ModifiersSubTotalAsMoneyObject($excluded = null, $stopAtExcludedModifier = false)
2892
    {
2893
        return EcommerceCurrency::get_money_object_from_order_currency($this->ModifiersSubTotal($excluded, $stopAtExcludedModifier), $this);
2894
    }
2895
2896
    /**
2897
     * Returns the total cost of an order including the additional charges or deductions of its modifiers.
2898
     *
2899
     * @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...
2900
     */
2901
    public function Total()
2902
    {
2903
        return $this->getTotal();
2904
    }
2905
    public function getTotal()
2906
    {
2907
        return $this->SubTotal() + $this->ModifiersSubTotal();
2908
    }
2909
2910
    /**
2911
     * @return Currency (DB Object)
2912
     **/
2913
    public function TotalAsCurrencyObject()
2914
    {
2915
        return DBField::create_field('Currency', $this->Total());
2916
    }
2917
2918
    /**
2919
     * @return Money
2920
     **/
2921
    public function TotalAsMoney()
2922
    {
2923
        return $this->getTotalAsMoney();
2924
    }
2925
    public function getTotalAsMoney()
2926
    {
2927
        return EcommerceCurrency::get_money_object_from_order_currency($this->Total(), $this);
2928
    }
2929
2930
    /**
2931
     * Checks to see if any payments have been made on this order
2932
     * and if so, subracts the payment amount from the order.
2933
     *
2934
     * @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...
2935
     **/
2936
    public function TotalOutstanding()
2937
    {
2938
        return $this->getTotalOutstanding();
2939
    }
2940
    public function getTotalOutstanding()
2941
    {
2942
        if ($this->IsSubmitted()) {
2943
            $total = $this->Total();
2944
            $paid = $this->TotalPaid();
2945
            $outstanding = $total - $paid;
2946
            $maxDifference = EcommerceConfig::get('Order', 'maximum_ignorable_sales_payments_difference');
2947
            if (abs($outstanding) < $maxDifference) {
2948
                $outstanding = 0;
2949
            }
2950
2951
            return floatval($outstanding);
2952
        } else {
2953
            return 0;
2954
        }
2955
    }
2956
2957
    /**
2958
     * @return Currency (DB Object)
2959
     **/
2960
    public function TotalOutstandingAsCurrencyObject()
2961
    {
2962
        return DBField::create_field('Currency', $this->TotalOutstanding());
2963
    }
2964
2965
    /**
2966
     * @return Money
2967
     **/
2968
    public function TotalOutstandingAsMoney()
2969
    {
2970
        return $this->getTotalOutstandingAsMoney();
2971
    }
2972
    public function getTotalOutstandingAsMoney()
2973
    {
2974
        return EcommerceCurrency::get_money_object_from_order_currency($this->TotalOutstanding(), $this);
2975
    }
2976
2977
    /**
2978
     * @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...
2979
     */
2980
    public function TotalPaid()
2981
    {
2982
        return $this->getTotalPaid();
2983
    }
2984
    public function getTotalPaid()
2985
    {
2986
        $paid = 0;
2987
        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...
2988
            foreach ($payments as $payment) {
2989
                if ($payment->Status == 'Success') {
2990
                    $paid += $payment->Amount->getAmount();
2991
                }
2992
            }
2993
        }
2994
        $reverseExchange = 1;
2995
        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...
2996
            $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...
2997
        }
2998
2999
        return $paid * $reverseExchange;
3000
    }
3001
3002
    /**
3003
     * @return Currency (DB Object)
3004
     **/
3005
    public function TotalPaidAsCurrencyObject()
3006
    {
3007
        return DBField::create_field('Currency', $this->TotalPaid());
3008
    }
3009
3010
    /**
3011
     * @return Money
3012
     **/
3013
    public function TotalPaidAsMoney()
3014
    {
3015
        return $this->getTotalPaidAsMoney();
3016
    }
3017
    public function getTotalPaidAsMoney()
3018
    {
3019
        return EcommerceCurrency::get_money_object_from_order_currency($this->TotalPaid(), $this);
3020
    }
3021
3022
    /**
3023
     * returns the total number of OrderItems (not modifiers).
3024
     * This is meant to run as fast as possible to quickly check
3025
     * if there is anything in the cart.
3026
     *
3027
     * @param bool $recalculate - do we need to recalculate (value is retained during lifetime of Object)
3028
     *
3029
     * @return int
3030
     **/
3031
    public function TotalItems($recalculate = false)
3032
    {
3033
        return $this->getTotalItems($recalculate);
3034
    }
3035
    public function getTotalItems($recalculate = false)
3036
    {
3037
        if ($this->totalItems === null || $recalculate) {
3038
            $this->totalItems = OrderItem::get()
3039
                ->where('"OrderAttribute"."OrderID" = '.$this->ID.' AND "OrderItem"."Quantity" > 0')
3040
                ->count();
3041
        }
3042
3043
        return $this->totalItems;
3044
    }
3045
3046
    /**
3047
     * Little shorthand.
3048
     *
3049
     * @param bool $recalculate
3050
     *
3051
     * @return bool
3052
     **/
3053
    public function MoreThanOneItemInCart($recalculate = false)
3054
    {
3055
        return $this->TotalItems($recalculate) > 1 ? true : false;
3056
    }
3057
3058
    /**
3059
     * returns the total number of OrderItems (not modifiers) times their respectective quantities.
3060
     *
3061
     * @param bool $recalculate - force recalculation
3062
     *
3063
     * @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...
3064
     **/
3065
    public function TotalItemsTimesQuantity($recalculate = false)
3066
    {
3067
        return $this->getTotalItemsTimesQuantity($recalculate);
3068
    }
3069
    public function getTotalItemsTimesQuantity($recalculate = false)
3070
    {
3071
        if ($this->totalItemsTimesQuantity === null || $recalculate) {
3072
            //to do, why do we check if you can edit ????
3073
            $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...
3074
                SELECT SUM("OrderItem"."Quantity")
3075
                FROM "OrderItem"
3076
                    INNER JOIN "OrderAttribute" ON "OrderAttribute"."ID" = "OrderItem"."ID"
3077
                WHERE
3078
                    "OrderAttribute"."OrderID" = '.$this->ID.'
3079
                    AND "OrderItem"."Quantity" > 0'
3080
            )->value();
3081
        }
3082
3083
        return $this->totalItemsTimesQuantity - 0;
3084
    }
3085
3086
    /**
3087
     *
3088
     * @return string (country code)
3089
     **/
3090
    public function Country()
3091
    {
3092
        return $this->getCountry();
3093
    }
3094
3095
    /**
3096
    * Returns the country code for the country that applies to the order.
3097
    * @alias  for getCountry
3098
    *
3099
    * @return string - country code e.g. NZ
3100
     */
3101
    public function getCountry()
3102
    {
3103
        $countryCodes = array(
3104
            'Billing' => '',
3105
            'Shipping' => '',
3106
        );
3107
        $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...
3108
        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...
3109
            $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...
3110
            if ($billingAddress) {
3111
                if ($billingAddress->Country) {
3112
                    $countryCodes['Billing'] = $billingAddress->Country;
3113
                }
3114
            }
3115
        }
3116
        if ($this->IsSeparateShippingAddress()) {
3117
            $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...
3118
            if ($shippingAddress) {
3119
                if ($shippingAddress->ShippingCountry) {
3120
                    $countryCodes['Shipping'] = $shippingAddress->ShippingCountry;
3121
                }
3122
            }
3123
        }
3124
        if (
3125
            (EcommerceConfig::get('OrderAddress', 'use_shipping_address_for_main_region_and_country') && $countryCodes['Shipping'])
3126
            ||
3127
            (!$countryCodes['Billing'] && $countryCodes['Shipping'])
3128
        ) {
3129
            $code = $countryCodes['Shipping'];
3130
        } elseif ($countryCodes['Billing']) {
3131
            $code = $countryCodes['Billing'];
3132
        } else {
3133
            $code = EcommerceCountry::get_country_from_ip();
3134
        }
3135
3136
        return $code;
3137
    }
3138
3139
    /**
3140
     * is this a gift / separate shippingAddress?
3141
     * @return Boolean
3142
     */
3143
    public function IsSeparateShippingAddress()
3144
    {
3145
        return $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...
3146
    }
3147
3148
    /**
3149
     * @alias for getFullNameCountry
3150
     *
3151
     * @return string - country name
3152
     **/
3153
    public function FullNameCountry()
3154
    {
3155
        return $this->getFullNameCountry();
3156
    }
3157
3158
    /**
3159
     * returns name of coutry.
3160
     *
3161
     * @return string - country name
3162
     **/
3163
    public function getFullNameCountry()
3164
    {
3165
        return EcommerceCountry::find_title($this->Country());
3166
    }
3167
3168
    /**
3169
     * @alis for getExpectedCountryName
3170
     * @return string - country name
3171
     **/
3172
    public function ExpectedCountryName()
3173
    {
3174
        return $this->getExpectedCountryName();
3175
    }
3176
3177
    /**
3178
     * returns name of coutry that we expect the customer to have
3179
     * This takes into consideration more than just what has been entered
3180
     * for example, it looks at GEO IP.
3181
     *
3182
     * @todo: why do we dont return a string IF there is only one item.
3183
     *
3184
     * @return string - country name
3185
     **/
3186
    public function getExpectedCountryName()
3187
    {
3188
        return EcommerceCountry::find_title(EcommerceCountry::get_country(false, $this->ID));
3189
    }
3190
3191
    /**
3192
     * return the title of the fixed country (if any).
3193
     *
3194
     * @return string | empty string
3195
     **/
3196
    public function FixedCountry()
3197
    {
3198
        return $this->getFixedCountry();
3199
    }
3200
    public function getFixedCountry()
3201
    {
3202
        $code = EcommerceCountry::get_fixed_country_code();
3203
        if ($code) {
3204
            return EcommerceCountry::find_title($code);
3205
        }
3206
3207
        return '';
3208
    }
3209
3210
    /**
3211
     * Returns the region that applies to the order.
3212
     * we check both billing and shipping, in case one of them is empty.
3213
     *
3214
     * @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...
3215
     **/
3216
    public function Region()
3217
    {
3218
        return $this->getRegion();
3219
    }
3220
    public function getRegion()
3221
    {
3222
        $regionIDs = array(
3223
            'Billing' => 0,
3224
            'Shipping' => 0,
3225
        );
3226
        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...
3227
            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...
3228
                if ($billingAddress->RegionID) {
3229
                    $regionIDs['Billing'] = $billingAddress->RegionID;
3230
                }
3231
            }
3232
        }
3233
        if ($this->CanHaveShippingAddress()) {
3234
            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...
3235
                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...
3236
                    if ($shippingAddress->ShippingRegionID) {
3237
                        $regionIDs['Shipping'] = $shippingAddress->ShippingRegionID;
3238
                    }
3239
                }
3240
            }
3241
        }
3242
        if (count($regionIDs)) {
3243
            //note the double-check with $this->CanHaveShippingAddress() and get_use_....
3244
            if ($this->CanHaveShippingAddress() && EcommerceConfig::get('OrderAddress', 'use_shipping_address_for_main_region_and_country') && $regionIDs['Shipping']) {
3245
                return EcommerceRegion::get()->byID($regionIDs['Shipping']);
3246
            } else {
3247
                return EcommerceRegion::get()->byID($regionIDs['Billing']);
3248
            }
3249
        } else {
3250
            return EcommerceRegion::get()->byID(EcommerceRegion::get_region_from_ip());
3251
        }
3252
    }
3253
3254
    /**
3255
     * Casted variable
3256
     * Currency is not the same as the standard one?
3257
     *
3258
     * @return bool
3259
     **/
3260
    public function HasAlternativeCurrency()
3261
    {
3262
        return $this->getHasAlternativeCurrency();
3263
    }
3264
    public function getHasAlternativeCurrency()
3265
    {
3266
        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...
3267
            if ($currency->IsDefault()) {
0 ignored issues
show
Coding Style introduced by
The if-else statement can be simplified to return !$currency->IsDefault();.
Loading history...
3268
                return false;
3269
            } else {
3270
                return true;
3271
            }
3272
        } else {
3273
            return false;
3274
        }
3275
    }
3276
3277
    /**
3278
     * Makes sure exchange rate is updated and maintained before order is submitted
3279
     * This method is public because it could be called from a shopping Cart Object.
3280
     **/
3281
    public function EnsureCorrectExchangeRate()
3282
    {
3283
        if (!$this->IsSubmitted()) {
3284
            $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...
3285
            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...
3286
                if ($currency->IsDefault()) {
3287
                    $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...
3288
                } else {
3289
                    $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...
3290
                }
3291
            } else {
3292
                $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...
3293
            }
3294
            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...
3295
                $this->write();
3296
            }
3297
        }
3298
    }
3299
3300
    /**
3301
     * speeds up processing by storing the IsSubmitted value
3302
     * we start with -1 to know if it has been requested before.
3303
     *
3304
     * @var bool
3305
     */
3306
    protected $_isSubmittedTempVar = -1;
3307
3308
    /**
3309
     * Casted variable - has the order been submitted?
3310
     * alias
3311
     * @param bool $recalculate
3312
     *
3313
     * @return bool
3314
     **/
3315
    public function IsSubmitted($recalculate = true)
3316
    {
3317
        return $this->getIsSubmitted($recalculate);
3318
    }
3319
3320
    /**
3321
     * Casted variable - has the order been submitted?
3322
     *
3323
     * @param bool $recalculate
3324
     *
3325
     * @return bool
3326
     **/
3327
    public function getIsSubmitted($recalculate = false)
3328
    {
3329
        if ($this->_isSubmittedTempVar === -1 || $recalculate) {
3330
            if ($this->SubmissionLog()) {
3331
                $this->_isSubmittedTempVar = true;
3332
            } else {
3333
                $this->_isSubmittedTempVar = false;
3334
            }
3335
        }
3336
3337
        return $this->_isSubmittedTempVar;
3338
    }
3339
3340
    /**
3341
     *
3342
     *
3343
     * @return bool
3344
     */
3345
    public function IsArchived()
3346
    {
3347
        $lastStep = OrderStep::last_order_step();
3348
        if ($lastStep) {
3349
            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...
3350
                return true;
3351
            }
3352
        }
3353
        return false;
3354
    }
3355
3356
    /**
3357
     * Submission Log for this Order (if any).
3358
     *
3359
     * @return Submission Log (OrderStatusLog_Submitted) | Null
3360
     **/
3361
    public function SubmissionLog()
3362
    {
3363
        $className = EcommerceConfig::get('OrderStatusLog', 'order_status_log_class_used_for_submitting_order');
3364
3365
        return $className::get()
3366
            ->Filter(array('OrderID' => $this->ID))
3367
            ->Last();
3368
    }
3369
3370
    /**
3371
     * Submission Log for this Order (if any).
3372
     *
3373
     * @return DateTime
0 ignored issues
show
Documentation introduced by
Should the return type not be DBField?

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

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