Completed
Push — master ( 796c30...92d109 )
by Nicolaas
03:51
created

Order::FeedbackLink()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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

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

namespace YourVendor;

class YourClass { }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading history...
175
    public function i18n_plural_name()
176
    {
177
        return _t('Order.ORDERS', 'Orders');
178
    }
179
180
    /**
181
     * Standard SS variable.
182
     *
183
     * @var string
184
     */
185
    private static $description = "A collection of items that together make up the 'Order'.  An order can be placed.";
0 ignored issues
show
Unused Code introduced by
The property $description is not used and could be removed.

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

Loading history...
186
187
    /**
188
     * Tells us if an order needs to be recalculated
189
     * can save one for each order...
190
     *
191
     * @var array
192
     */
193
    private static $_needs_recalculating = array();
194
195
    /**
196
     * @param bool (optional) $b
197
     * @param int (optional)  $orderID
198
     *
199
     * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|null?

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

Loading history...
200
     */
201
    public static function set_needs_recalculating($b = true, $orderID = 0)
202
    {
203
        self::$_needs_recalculating[$orderID] = $b;
204
    }
205
206
    /**
207
     * @param int (optional) $orderID
208
     *
209
     * @return bool
210
     */
211
    public static function get_needs_recalculating($orderID = 0)
212
    {
213
        return isset(self::$_needs_recalculating[$orderID]) ? self::$_needs_recalculating[$orderID] : false;
214
    }
215
216
    /**
217
     * Total Items : total items in cart
218
     * We start with -1 to easily identify if it has been run before.
219
     *
220
     * @var int
221
     */
222
    protected $totalItems = null;
223
224
    /**
225
     * Total Items : total items in cart
226
     * We start with -1 to easily identify if it has been run before.
227
     *
228
     * @var float
229
     */
230
    protected $totalItemsTimesQuantity = null;
231
232
    /**
233
     * Returns a set of modifier forms for use in the checkout order form,
234
     * Controller is optional, because the orderForm has its own default controller.
235
     *
236
     * This method only returns the Forms that should be included outside
237
     * the editable table... Forms within it can be called
238
     * from through the modifier itself.
239
     *
240
     * @param Controller $optionalController
0 ignored issues
show
Documentation introduced by
Should the type for parameter $optionalController not be null|Controller?

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

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

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

Loading history...
241
     * @param Validator  $optionalValidator
0 ignored issues
show
Documentation introduced by
Should the type for parameter $optionalValidator not be null|Validator?

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

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

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

Loading history...
242
     *
243
     * @return ArrayList (ModifierForms) | Null
0 ignored issues
show
Documentation introduced by
Should the return type not be ArrayList|null?

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

Loading history...
244
     **/
245
    public function getModifierForms(Controller $optionalController = null, Validator $optionalValidator = null)
246
    {
247
        $arrayList = new ArrayList();
248
        $modifiers = $this->Modifiers();
249
        if ($modifiers->count()) {
250
            foreach ($modifiers as $modifier) {
251
                if ($modifier->ShowForm()) {
252
                    if ($form = $modifier->getModifierForm($optionalController, $optionalValidator)) {
253
                        $form->ShowFormInEditableOrderTable = $modifier->ShowFormInEditableOrderTable();
254
                        $form->ShowFormOutsideEditableOrderTable = $modifier->ShowFormOutsideEditableOrderTable();
255
                        $form->ModifierName = $modifier->ClassName;
256
                        $arrayList->push($form);
257
                    }
258
                }
259
            }
260
        }
261
        if ($arrayList->count()) {
262
            return $arrayList;
263
        } else {
264
            return;
265
        }
266
    }
267
268
    /**
269
     * This function returns the OrderSteps.
270
     *
271
     * @return ArrayList (OrderSteps)
0 ignored issues
show
Documentation introduced by
Should the return type not be DataList?

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

Loading history...
272
     **/
273
    public static function get_order_status_options()
274
    {
275
        return OrderStep::get();
276
    }
277
278
    /**
279
     * Like the standard byID, but it checks whether we are allowed to view the order.
280
     *
281
     * @return: Order | Null
282
     **/
283
    public static function get_by_id_if_can_view($id)
284
    {
285
        $order = Order::get()->byID($id);
286
        if ($order && $order->canView()) {
287
            if ($order->IsSubmitted()) {
288
                // LITTLE HACK TO MAKE SURE WE SHOW THE LATEST INFORMATION!
289
                $order->tryToFinaliseOrder();
290
            }
291
292
            return $order;
293
        }
294
295
        return;
296
    }
297
298
    /**
299
     * returns a Datalist with the submitted order log included
300
     * this allows you to sort the orders by their submit dates.
301
     * You can retrieve this list and then add more to it (e.g. additional filters, additional joins, etc...).
302
     *
303
     * @param bool $onlySubmittedOrders - only include Orders that have already been submitted.
304
     * @param bool $includeCancelledOrders - only include Orders that have already been submitted.
305
     *
306
     * @return DataList (Orders)
307
     */
308
    public static function get_datalist_of_orders_with_submit_record($onlySubmittedOrders = true, $includeCancelledOrders = false)
309
    {
310
        if ($onlySubmittedOrders) {
311
            $submittedOrderStatusLogClassName = EcommerceConfig::get('OrderStatusLog', 'order_status_log_class_used_for_submitting_order');
312
            $list = Order::get()
313
                ->LeftJoin('OrderStatusLog', '"Order"."ID" = "OrderStatusLog"."OrderID"')
314
                ->LeftJoin($submittedOrderStatusLogClassName, '"OrderStatusLog"."ID" = "'.$submittedOrderStatusLogClassName.'"."ID"')
315
                ->Sort('OrderStatusLog.Created', 'ASC');
316
            $where = ' ("OrderStatusLog"."ClassName" = \''.$submittedOrderStatusLogClassName.'\') ';
317
        } else {
318
            $list = Order::get();
319
            $where = ' ("StatusID" > 0) ';
320
        }
321
        if ($includeCancelledOrders) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

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

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

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

could be turned into

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

This is much more concise to read.

Loading history...
322
            //do nothing...
323
        } else {
324
            $where .= ' AND ("CancelledByID" = 0 OR "CancelledByID" IS NULL)';
325
        }
326
        $list = $list->where($where);
327
328
        return $list;
329
    }
330
331
/*******************************************************
332
   * 1. CMS STUFF
333
*******************************************************/
334
335
    /**
336
     * fields that we remove from the parent::getCMSFields object set.
337
     *
338
     * @var array
339
     */
340
    protected $fieldsAndTabsToBeRemoved = array(
341
        'MemberID',
342
        'Attributes',
343
        'SessionID',
344
        'Emails',
345
        'BillingAddressID',
346
        'ShippingAddressID',
347
        'UseShippingAddress',
348
        'OrderStatusLogs',
349
        'Payments',
350
        'OrderDate',
351
        'ExchangeRate',
352
        'CurrencyUsedID',
353
        'StatusID',
354
        'Currency',
355
    );
356
357
    /**
358
     * STANDARD SILVERSTRIPE STUFF.
359
     **/
360
    private static $summary_fields = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $summary_fields is not used and could be removed.

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

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

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

Loading history...
376
        'ID' => array(
377
            'field' => 'NumericField',
378
            'title' => 'Order Number',
379
        ),
380
        'MemberID' => array(
381
            'field' => 'TextField',
382
            'filter' => 'OrderFilters_MemberAndAddress',
383
            'title' => 'Customer Details',
384
        ),
385
        'Created' => array(
386
            'field' => 'TextField',
387
            'filter' => 'OrderFilters_AroundDateFilter',
388
            'title' => 'Date (e.g. Today, 1 jan 2007, or last week)',
389
        ),
390
        //make sure to keep the items below, otherwise they do not show in form
391
        'StatusID' => array(
392
            'filter' => 'OrderFilters_MultiOptionsetStatusIDFilter',
393
        ),
394
        'CancelledByID' => array(
395
            'filter' => 'OrderFilters_HasBeenCancelled',
396
            'title' => 'Cancelled by ...',
397
        ),
398
    );
399
400
    /**
401
     * Determine which properties on the DataObject are
402
     * searchable, and map them to their default {@link FormField}
403
     * representations. Used for scaffolding a searchform for {@link ModelAdmin}.
404
     *
405
     * Some additional logic is included for switching field labels, based on
406
     * how generic or specific the field type is.
407
     *
408
     * Used by {@link SearchContext}.
409
     *
410
     * @param array $_params
0 ignored issues
show
Documentation introduced by
Should the type for parameter $_params not be array|null?

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

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

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

Loading history...
411
     *                       'fieldClasses': Associative array of field names as keys and FormField classes as values
412
     *                       'restrictFields': Numeric array of a field name whitelist
413
     *
414
     * @return FieldList
415
     */
416
    public function scaffoldSearchFields($_params = null)
0 ignored issues
show
Coding Style introduced by
scaffoldSearchFields uses the super-global variable $_GET which is generally not recommended.

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

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

// Better
class Router
{
    private $host;

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

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

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

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
417
    {
418
        $fieldList = parent::scaffoldSearchFields($_params);
419
420
        //for sales to action only show relevant ones ...
421
        if(Controller::curr() && Controller::curr()->class === 'SalesAdmin') {
422
            $statusOptions = OrderStep::admin_manageable_steps();
423
        } else {
424
            $statusOptions = OrderStep::get();
425
        }
426
        if ($statusOptions && $statusOptions->count()) {
427
            $createdOrderStatusID = 0;
0 ignored issues
show
Unused Code introduced by
$createdOrderStatusID is not used, you could remove the assignment.

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

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

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

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

Loading history...
428
            $preSelected = array();
429
            $createdOrderStatus = $statusOptions->First();
430
            if ($createdOrderStatus) {
431
                $createdOrderStatusID = $createdOrderStatus->ID;
0 ignored issues
show
Unused Code introduced by
$createdOrderStatusID is not used, you could remove the assignment.

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

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

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

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

Loading history...
432
            }
433
            $arrayOfStatusOptions = clone $statusOptions->map('ID', 'Title');
434
            $arrayOfStatusOptionsFinal = array();
435
            if (count($arrayOfStatusOptions)) {
436
                foreach ($arrayOfStatusOptions as $key => $value) {
437
                    if (isset($_GET['q']['StatusID'][$key])) {
438
                        $preSelected[$key] = $key;
439
                    }
440
                    $count = Order::get()
441
                        ->Filter(array('StatusID' => intval($key)))
442
                        ->count();
443
                    if ($count < 1) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

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

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

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

could be turned into

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

This is much more concise to read.

Loading history...
444
                        //do nothing
445
                    } else {
446
                        $arrayOfStatusOptionsFinal[$key] = $value." ($count)";
447
                    }
448
                }
449
            }
450
            $statusField = new CheckboxSetField(
451
                'StatusID',
452
                Injector::inst()->get('OrderStep')->i18n_singular_name(),
453
                $arrayOfStatusOptionsFinal,
454
                $preSelected
0 ignored issues
show
Documentation introduced by
$preSelected is of type array, but the function expects a string.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

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

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

Loading history...
1306
     */
1307
    public function CurrentStepVisibleToCustomer()
1308
    {
1309
        $obj = $this->MyStep();
1310
        if ($obj->HideStepFromCustomer) {
1311
            $obj = OrderStep::get()->where('"OrderStep"."Sort" < '.$obj->Sort.' AND "HideStepFromCustomer" = 0')->Last();
1312
            if (!$obj) {
1313
                $obj = OrderStep::get()->First();
1314
            }
1315
        }
1316
1317
        return $obj;
1318
    }
1319
1320
    /**
1321
     * works out if the order is still at the first OrderStep.
1322
     *
1323
     * @return bool
1324
     */
1325
    public function IsFirstStep()
1326
    {
1327
        $firstStep = OrderStep::get()->First();
1328
        $currentStep = $this->MyStep();
1329
        if ($firstStep && $currentStep) {
1330
            if ($firstStep->ID == $currentStep->ID) {
1331
                return true;
1332
            }
1333
        }
1334
1335
        return false;
1336
    }
1337
1338
    /**
1339
     * Is the order still being "edited" by the customer?
1340
     *
1341
     * @return bool
1342
     */
1343
    public function IsInCart()
1344
    {
1345
        return (bool) $this->IsSubmitted() ? false : true;
1346
    }
1347
1348
    /**
1349
     * The order has "passed" the IsInCart phase.
1350
     *
1351
     * @return bool
1352
     */
1353
    public function IsPastCart()
1354
    {
1355
        return (bool) $this->IsInCart() ? false : true;
1356
    }
1357
1358
    /**
1359
     * Are there still steps the order needs to go through?
1360
     *
1361
     * @return bool
1362
     */
1363
    public function IsUncomplete()
1364
    {
1365
        return (bool) $this->MyStep()->ShowAsUncompletedOrder;
1366
    }
1367
1368
    /**
1369
     * Is the order in the :"processing" phaase.?
1370
     *
1371
     * @return bool
1372
     */
1373
    public function IsProcessing()
1374
    {
1375
        return (bool) $this->MyStep()->ShowAsInProcessOrder;
1376
    }
1377
1378
    /**
1379
     * Is the order completed?
1380
     *
1381
     * @return bool
1382
     */
1383
    public function IsCompleted()
1384
    {
1385
        return (bool) $this->MyStep()->ShowAsCompletedOrder;
1386
    }
1387
1388
    /**
1389
     * Has the order been paid?
1390
     * TODO: why do we check if there is a total at all?
1391
     *
1392
     * @return bool
1393
     */
1394
    public function IsPaid()
1395
    {
1396
        if ($this->IsSubmitted()) {
1397
            return (bool) (($this->Total() >= 0) && ($this->TotalOutstanding() <= 0));
1398
        }
1399
1400
        return false;
1401
    }
1402
    /**
1403
     * Has the order been paid?
1404
     * TODO: why do we check if there is a total at all?
1405
     *
1406
     * @return Boolean (object)
0 ignored issues
show
Documentation introduced by
Should the return type not be DBField?

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

Loading history...
1407
     */
1408
    public function IsPaidNice()
1409
    {
1410
        return  DBField::create_field('Boolean', $this->IsPaid());
1411
    }
1412
1413
    /**
1414
     * Has the order been paid?
1415
     * TODO: why do we check if there is a total at all?
1416
     *
1417
     * @return bool
1418
     */
1419
    public function PaymentIsPending()
1420
    {
1421
        if ($this->IsSubmitted()) {
1422
            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...
1423
                //do nothing;
1424
            } 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...
1425
                foreach ($payments as $payment) {
1426
                    if ('Pending' == $payment->Status) {
1427
                        return true;
1428
                    }
1429
                }
1430
            }
1431
        }
1432
1433
        return false;
1434
    }
1435
1436
    /**
1437
     * shows payments that are meaningfull
1438
     * if the order has been paid then only show successful payments.
1439
     *
1440
     * @return DataList
1441
     */
1442
    public function RelevantPayments()
1443
    {
1444
        if ($this->IsPaid()) {
1445
            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...
1446
            //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...
1447
            //	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...
1448
        } else {
1449
            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...
1450
        }
1451
    }
1452
1453
    /**
1454
     * Has the order been cancelled?
1455
     *
1456
     * @return bool
1457
     */
1458
    public function IsCancelled()
1459
    {
1460
        return $this->getIsCancelled();
1461
    }
1462
    public function getIsCancelled()
1463
    {
1464
        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...
1465
    }
1466
1467
    /**
1468
     * Has the order been cancelled by the customer?
1469
     *
1470
     * @return bool
1471
     */
1472
    public function IsCustomerCancelled()
1473
    {
1474
        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...
1475
            return true;
1476
        }
1477
1478
        return false;
1479
    }
1480
1481
    /**
1482
     * Has the order been cancelled by the  administrator?
1483
     *
1484
     * @return bool
1485
     */
1486
    public function IsAdminCancelled()
1487
    {
1488
        if ($this->IsCancelled()) {
1489
            if (!$this->IsCustomerCancelled()) {
1490
                $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...
1491
                if ($admin) {
1492
                    if ($admin->IsShopAdmin()) {
1493
                        return true;
1494
                    }
1495
                }
1496
            }
1497
        }
1498
1499
        return false;
1500
    }
1501
1502
    /**
1503
     * Is the Shop Closed for business?
1504
     *
1505
     * @return bool
1506
     */
1507
    public function ShopClosed()
1508
    {
1509
        return EcomConfig()->ShopClosed;
1510
    }
1511
1512
/*******************************************************
1513
   * 4. LINKING ORDER WITH MEMBER AND ADDRESS
1514
*******************************************************/
1515
1516
    /**
1517
     * Returns a member linked to the order.
1518
     * If a member is already linked, it will return the existing member.
1519
     * Otherwise it will return a new Member.
1520
     *
1521
     * Any new member is NOT written, because we dont want to create a new member unless we have to!
1522
     * We will not add a member to the order unless a new one is created in the checkout
1523
     * OR the member is logged in / logs in.
1524
     *
1525
     * Also note that if a new member is created, it is not automatically written
1526
     *
1527
     * @param bool $forceCreation - if set to true then the member will always be saved in the database.
1528
     *
1529
     * @return Member
1530
     **/
1531
    public function CreateOrReturnExistingMember($forceCreation = false)
1532
    {
1533
        if ($this->IsSubmitted()) {
1534
            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...
1535
        }
1536
        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...
1537
            $member = $this->Member();
0 ignored issues
show
Bug introduced by
The method Member() does not exist on Order. Did you maybe mean CreateOrReturnExistingMember()?

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

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

Loading history...
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...
1538
        } elseif ($member = Member::currentUser()) {
1539
            if (!$member->IsShopAdmin()) {
1540
                $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...
1541
                $this->write();
1542
            }
1543
        }
1544
        $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...
1545
        if (!$member) {
1546
            $member = new Member();
1547
        }
1548
        if ($member && $forceCreation) {
1549
            $member->write();
1550
        }
1551
1552
        return $member;
1553
    }
1554
1555
    /**
1556
     * Returns either the existing one or a new Order Address...
1557
     * All Orders will have a Shipping and Billing address attached to it.
1558
     * Method used to retrieve object e.g. for $order->BillingAddress(); "BillingAddress" is the method name you can use.
1559
     * If the method name is the same as the class name then dont worry about providing one.
1560
     *
1561
     * @param string $className             - ClassName of the Address (e.g. BillingAddress or ShippingAddress)
1562
     * @param string $alternativeMethodName - method to retrieve Address
1563
     **/
1564
    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...
1565
    {
1566
        if ($this->exists()) {
1567
            $methodName = $className;
1568
            if ($alternativeMethodName) {
1569
                $methodName = $alternativeMethodName;
1570
            }
1571
            if ($this->IsSubmitted()) {
1572
                return $this->$methodName();
1573
            }
1574
            $variableName = $className.'ID';
1575
            $address = null;
1576
            if ($this->$variableName) {
1577
                $address = $this->$methodName();
1578
            }
1579
            if (!$address) {
1580
                $address = new $className();
1581
                if ($member = $this->CreateOrReturnExistingMember()) {
1582
                    if ($member->exists()) {
1583
                        $address->FillWithLastAddressFromMember($member, $write = false);
1584
                    }
1585
                }
1586
            }
1587
            if ($address) {
1588
                if (!$address->exists()) {
1589
                    $address->write();
1590
                }
1591
                if ($address->OrderID != $this->ID) {
1592
                    $address->OrderID = $this->ID;
1593
                    $address->write();
1594
                }
1595
                if ($this->$variableName != $address->ID) {
1596
                    if (!$this->IsSubmitted()) {
1597
                        $this->$variableName = $address->ID;
1598
                        $this->write();
1599
                    }
1600
                }
1601
1602
                return $address;
1603
            }
1604
        }
1605
1606
        return;
1607
    }
1608
1609
    /**
1610
     * Sets the country in the billing and shipping address.
1611
     *
1612
     * @param string $countryCode            - code for the country e.g. NZ
1613
     * @param bool   $includeBillingAddress
1614
     * @param bool   $includeShippingAddress
1615
     **/
1616
    public function SetCountryFields($countryCode, $includeBillingAddress = true, $includeShippingAddress = true)
1617
    {
1618
        if ($this->IsSubmitted()) {
1619
            user_error('Can not change country in submitted order', E_USER_NOTICE);
1620
        } else {
1621
            if ($includeBillingAddress) {
1622
                if ($billingAddress = $this->CreateOrReturnExistingAddress('BillingAddress')) {
1623
                    $billingAddress->SetCountryFields($countryCode);
1624
                }
1625
            }
1626
            if (EcommerceConfig::get('OrderAddress', 'use_separate_shipping_address')) {
1627
                if ($includeShippingAddress) {
1628
                    if ($shippingAddress = $this->CreateOrReturnExistingAddress('ShippingAddress')) {
1629
                        $shippingAddress->SetCountryFields($countryCode);
1630
                    }
1631
                }
1632
            }
1633
        }
1634
    }
1635
1636
    /**
1637
     * Sets the region in the billing and shipping address.
1638
     *
1639
     * @param int $regionID - ID for the region to be set
1640
     **/
1641
    public function SetRegionFields($regionID)
1642
    {
1643
        if ($this->IsSubmitted()) {
1644
            user_error('Can not change country in submitted order', E_USER_NOTICE);
1645
        } else {
1646
            if ($billingAddress = $this->CreateOrReturnExistingAddress('BillingAddress')) {
1647
                $billingAddress->SetRegionFields($regionID);
1648
            }
1649
            if ($this->CanHaveShippingAddress()) {
1650
                if ($shippingAddress = $this->CreateOrReturnExistingAddress('ShippingAddress')) {
1651
                    $shippingAddress->SetRegionFields($regionID);
1652
                }
1653
            }
1654
        }
1655
    }
1656
1657
    /**
1658
     * Stores the preferred currency of the order.
1659
     * IMPORTANTLY we store the exchange rate for future reference...
1660
     *
1661
     * @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...
1662
     */
1663
    public function UpdateCurrency($newCurrency)
1664
    {
1665
        if ($this->IsSubmitted()) {
1666
            user_error('Can not set the currency after the order has been submitted', E_USER_NOTICE);
1667
        } else {
1668
            if (! is_a($newCurrency, Object::getCustomClass('EcommerceCurrency'))) {
1669
                $newCurrency = EcommerceCurrency::default_currency();
1670
            }
1671
            $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...
1672
            $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...
1673
            $this->write();
1674
        }
1675
    }
1676
1677
    /**
1678
     * alias for UpdateCurrency.
1679
     *
1680
     * @param EcommerceCurrency $currency
1681
     */
1682
    public function SetCurrency($currency)
1683
    {
1684
        $this->UpdateCurrency($currency);
1685
    }
1686
1687
/*******************************************************
1688
   * 5. CUSTOMER COMMUNICATION
1689
*******************************************************/
1690
1691
    /**
1692
     * Send the invoice of the order by email.
1693
     *
1694
     * @param string $emailClassName     (optional) class used to send email
1695
     * @param string $subject            (optional) subject for the email
1696
     * @param string $message            (optional) the main message in the email
1697
     * @param bool   $resend             (optional) send the email even if it has been sent before
1698
     * @param bool   $adminOnlyOrToEmail (optional) sends the email to the ADMIN ONLY, if you provide an email, it will go to the email...
1699
     *
1700
     * @return bool TRUE on success, FALSE on failure
1701
     */
1702
    public function sendEmail(
1703
        $emailClassName = 'Order_InvoiceEmail',
1704
        $subject = '',
1705
        $message = '',
1706
        $resend = false,
1707
        $adminOnlyOrToEmail = false
1708
    ) {
1709
        return $this->prepareAndSendEmail(
1710
            $emailClassName,
1711
            $subject,
1712
            $message,
1713
            $resend,
1714
            $adminOnlyOrToEmail
1715
        );
1716
    }
1717
1718
    /**
1719
     * Sends a message to the shop admin ONLY and not to the customer
1720
     * This can be used by ordersteps and orderlogs to notify the admin of any potential problems.
1721
     *
1722
     * @param string         $emailClassName       - (optional) template to be used ...
1723
     * @param string         $subject              - (optional) subject for the email
1724
     * @param string         $message              - (optional) message to be added with the email
1725
     * @param bool           $resend               - (optional) can it be sent twice?
1726
     * @param bool | string  $adminOnlyOrToEmail   - (optional) sends the email to the ADMIN ONLY, if you provide an email, it will go to the email...
1727
     *
1728
     * @return bool TRUE for success, FALSE for failure (not tested)
1729
     */
1730
    public function sendAdminNotification(
1731
        $emailClassName = 'Order_ErrorEmail',
1732
        $subject = '',
1733
        $message = '',
1734
        $resend = false,
1735
        $adminOnlyOrToEmail = true
1736
    ) {
1737
        return $this->prepareAndSendEmail(
1738
            $emailClassName,
1739
            $subject,
1740
            $message,
1741
            $resend,
1742
            $adminOnlyOrToEmail
1743
        );
1744
    }
1745
1746
    /**
1747
     * returns the order formatted as an email.
1748
     *
1749
     * @param string $emailClassName - template to use.
1750
     * @param string $subject        - (optional) the subject (which can be used as title in email)
1751
     * @param string $message        - (optional) the additional message
1752
     *
1753
     * @return string (html)
1754
     */
1755
    public function renderOrderInEmailFormat(
1756
        $emailClassName,
1757
        $subject = '',
1758
        $message = ''
1759
    )
1760
    {
1761
        $arrayData = $this->createReplacementArrayForEmail($subject, $message);
1762
        Config::nest();
1763
        Config::inst()->update('SSViewer', 'theme_enabled', true);
1764
        $html = $arrayData->renderWith($emailClassName);
1765
        Config::unnest();
1766
1767
        return Order_Email::emogrify_html($html);
1768
    }
1769
1770
    /**
1771
     * Send a mail of the order to the client (and another to the admin).
1772
     *
1773
     * @param string         $emailClassName       - (optional) template to be used ...
1774
     * @param string         $subject              - (optional) subject for the email
1775
     * @param string         $message              - (optional) message to be added with the email
1776
     * @param bool           $resend               - (optional) can it be sent twice?
1777
     * @param bool | string  $adminOnlyOrToEmail   - (optional) sends the email to the ADMIN ONLY, if you provide an email, it will go to the email...
1778
     *
1779
     * @return bool TRUE for success, FALSE for failure (not tested)
1780
     */
1781
    protected function prepareAndSendEmail(
1782
        $emailClassName,
1783
        $subject,
1784
        $message,
1785
        $resend = false,
1786
        $adminOnlyOrToEmail = false
1787
    ) {
1788
        $arrayData = $this->createReplacementArrayForEmail($subject, $message);
1789
        $from = Order_Email::get_from_email();
1790
        //why are we using this email and NOT the member.EMAIL?
1791
        //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...
1792
        if ($adminOnlyOrToEmail) {
1793
            if (filter_var($adminOnlyOrToEmail, FILTER_VALIDATE_EMAIL)) {
1794
                $to = $adminOnlyOrToEmail;
1795
                // invalid e-mail address
1796
            } else {
1797
                $to = Order_Email::get_from_email();
1798
            }
1799
        } else {
1800
            $to = $this->getOrderEmail();
1801
        }
1802
        if ($from && $to) {
1803
            $email = new $emailClassName();
1804
            if (!(is_a($email, Object::getCustomClass('Email')))) {
1805
                user_error('No correct email class provided.', E_USER_ERROR);
1806
            }
1807
            $email->setFrom($from);
1808
            $email->setTo($to);
1809
            //we take the subject from the Array Data, just in case it has been adjusted.
1810
            $email->setSubject($arrayData->getField('Subject'));
1811
            //we also see if a CC and a BCC have been added
1812
            ;
1813
            if ($cc = $arrayData->getField('CC')) {
1814
                $email->setCc($cc);
1815
            }
1816
            if ($bcc = $arrayData->getField('BCC')) {
1817
                $email->setBcc($bcc);
1818
            }
1819
            $email->populateTemplate($arrayData);
1820
            // This might be called from within the CMS,
1821
            // so we need to restore the theme, just in case
1822
            // templates within the theme exist
1823
            Config::nest();
1824
            Config::inst()->update('SSViewer', 'theme_enabled', true);
1825
            $email->setOrder($this);
1826
            $email->setResend($resend);
1827
            $result = $email->send(null);
1828
            Config::unnest();
1829
            if (Director::isDev()) {
1830
                return true;
1831
            } else {
1832
                return $result;
1833
            }
1834
        }
1835
1836
        return false;
1837
    }
1838
1839
    /**
1840
     * returns the Data that can be used in the body of an order Email
1841
     * we add the subject here so that the subject, for example, can be added to the <title>
1842
     * of the email template.
1843
     * we add the subject here so that the subject, for example, can be added to the <title>
1844
     * of the email template.
1845
     *
1846
     * @param string $subject  - (optional) subject for email
1847
     * @param string $message  - (optional) the additional message
1848
     *
1849
     * @return ArrayData
1850
     *                   - Subject - EmailSubject
1851
     *                   - Message - specific message for this order
1852
     *                   - Message - custom message
1853
     *                   - OrderStepMessage - generic message for step
1854
     *                   - Order
1855
     *                   - EmailLogo
1856
     *                   - ShopPhysicalAddress
1857
     *                   - CurrentDateAndTime
1858
     *                   - BaseURL
1859
     *                   - CC
1860
     *                   - BCC
1861
     */
1862
    protected function createReplacementArrayForEmail($subject = '', $message = '')
1863
    {
1864
        $step = $this->MyStep();
1865
        $config = $this->EcomConfig();
1866
        $replacementArray = array();
1867
        //set subject
1868
        if ( ! $subject) {
1869
            $subject = $step->EmailSubject;
1870
        }
1871
        if( ! $message) {
1872
            $message = $step->CustomerMessage;
1873
        }
1874
        $subject = str_replace('[OrderNumber]', $this->ID, $subject);
1875
        //set other variables
1876
        $replacementArray['Subject'] = $subject;
1877
        $replacementArray['To'] = '';
1878
        $replacementArray['CC'] = '';
1879
        $replacementArray['BCC'] = '';
1880
        $replacementArray['OrderStepMessage'] = $message;
1881
        $replacementArray['Order'] = $this;
1882
        $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...
1883
        $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...
1884
        $replacementArray['CurrentDateAndTime'] = DBField::create_field('SS_Datetime', 'Now');
1885
        $replacementArray['BaseURL'] = Director::baseURL();
1886
        $arrayData = ArrayData::create($replacementArray);
1887
        $this->extend('updateReplacementArrayForEmail', $arrayData);
1888
1889
        return $arrayData;
1890
    }
1891
1892
/*******************************************************
1893
   * 6. ITEM MANAGEMENT
1894
*******************************************************/
1895
1896
    /**
1897
     * returns a list of Order Attributes by type.
1898
     *
1899
     * @param array | String $types
1900
     *
1901
     * @return ArrayList
1902
     */
1903
    public function getOrderAttributesByType($types)
1904
    {
1905
        if (!is_array($types) && is_string($types)) {
1906
            $types = array($types);
1907
        }
1908
        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...
1909
            user_error('wrong parameter (types) provided in Order::getOrderAttributesByTypes');
1910
        }
1911
        $al = new ArrayList();
1912
        $items = $this->Items();
1913
        foreach ($items as $item) {
1914
            if (in_array($item->OrderAttributeType(), $types)) {
1915
                $al->push($item);
1916
            }
1917
        }
1918
        $modifiers = $this->Modifiers();
1919
        foreach ($modifiers as $modifier) {
1920
            if (in_array($modifier->OrderAttributeType(), $types)) {
1921
                $al->push($modifier);
1922
            }
1923
        }
1924
1925
        return $al;
1926
    }
1927
1928
    /**
1929
     * Returns the items of the order.
1930
     * Items are the order items (products) and NOT the modifiers (discount, tax, etc...).
1931
     *
1932
     * N. B. this method returns Order Items
1933
     * also see Buaybles
1934
1935
     *
1936
     * @param string filter - where statement to exclude certain items OR ClassName (e.g. 'TaxModifier')
1937
     *
1938
     * @return DataList (OrderItems)
1939
     */
1940
    public function Items($filterOrClassName = '')
1941
    {
1942
        if (!$this->exists()) {
1943
            $this->write();
1944
        }
1945
1946
        return $this->itemsFromDatabase($filterOrClassName);
1947
    }
1948
1949
    /**
1950
     * @alias function of Items
1951
     *
1952
     * N. B. this method returns Order Items
1953
     * also see Buaybles
1954
     *
1955
     * @param string filter - where statement to exclude certain items.
1956
     * @alias for Items
1957
     * @return DataList (OrderItems)
1958
     */
1959
    public function OrderItems($filterOrClassName = '')
1960
    {
1961
        return $this->Items($filterOrClassName);
1962
    }
1963
1964
    /**
1965
     * returns the buyables asscoiated with the order items.
1966
     *
1967
     * NB. this method retursn buyables
1968
     *
1969
     * @param string filter - where statement to exclude certain items.
1970
     *
1971
     * @return ArrayList (Buyables)
1972
     */
1973
    public function Buyables($filterOrClassName = '')
1974
    {
1975
        $items = $this->Items($filterOrClassName);
1976
        $arrayList = new ArrayList();
1977
        foreach ($items as $item) {
1978
            $arrayList->push($item->Buyable());
1979
        }
1980
1981
        return $arrayList;
1982
    }
1983
1984
    /**
1985
     * Return all the {@link OrderItem} instances that are
1986
     * available as records in the database.
1987
     *
1988
     * @param string filter - where statement to exclude certain items,
1989
     *   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)
1990
     *
1991
     * @return DataList (OrderItems)
1992
     */
1993
    protected function itemsFromDatabase($filterOrClassName = '')
1994
    {
1995
        $className = 'OrderItem';
1996
        $extrafilter = '';
1997
        if ($filterOrClassName) {
1998
            if (class_exists($filterOrClassName)) {
1999
                $className = $filterOrClassName;
2000
            } else {
2001
                $extrafilter = " AND $filterOrClassName";
2002
            }
2003
        }
2004
2005
        return $className::get()->filter(array('OrderID' => $this->ID))->where($extrafilter);
2006
    }
2007
2008
    /**
2009
     * @alias for Modifiers
2010
     *
2011
     * @return DataList (OrderModifiers)
2012
     */
2013
    public function OrderModifiers()
2014
    {
2015
        return $this->Modifiers();
2016
    }
2017
2018
    /**
2019
     * Returns the modifiers of the order, if it hasn't been saved yet
2020
     * it returns the modifiers from session, if it has, it returns them
2021
     * from the DB entry. ONLY USE OUTSIDE ORDER.
2022
     *
2023
     * @param string filter - where statement to exclude certain items OR ClassName (e.g. 'TaxModifier')
2024
     *
2025
     * @return DataList (OrderModifiers)
2026
     */
2027
    public function Modifiers($filterOrClassName = '')
2028
    {
2029
        return $this->modifiersFromDatabase($filterOrClassName);
2030
    }
2031
2032
    /**
2033
     * Get all {@link OrderModifier} instances that are
2034
     * available as records in the database.
2035
     * NOTE: includes REMOVED Modifiers, so that they do not get added again...
2036
     *
2037
     * @param string filter - where statement to exclude certain items OR ClassName (e.g. 'TaxModifier')
2038
     *
2039
     * @return DataList (OrderModifiers)
2040
     */
2041
    protected function modifiersFromDatabase($filterOrClassName = '')
2042
    {
2043
        $className = 'OrderModifier';
2044
        $extrafilter = '';
2045
        if ($filterOrClassName) {
2046
            if (class_exists($filterOrClassName)) {
2047
                $className = $filterOrClassName;
2048
            } else {
2049
                $extrafilter = " AND $filterOrClassName";
2050
            }
2051
        }
2052
2053
        return $className::get()->where('"OrderAttribute"."OrderID" = '.$this->ID." $extrafilter");
2054
    }
2055
2056
    /**
2057
     * Calculates and updates all the order attributes.
2058
     *
2059
     * @param bool $recalculate - run it, even if it has run already
2060
     */
2061
    public function calculateOrderAttributes($recalculate = false)
2062
    {
2063
        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...
2064
            //submitted orders are NEVER recalculated.
2065
            //they are set in stone.
2066
        } elseif (self::get_needs_recalculating($this->ID) || $recalculate) {
2067
            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...
2068
                $this->ensureCorrectExchangeRate();
2069
                $this->calculateOrderItems($recalculate);
2070
                $this->calculateModifiers($recalculate);
2071
                $this->extend('onCalculateOrder');
2072
            }
2073
        }
2074
    }
2075
2076
    /**
2077
     * Calculates and updates all the product items.
2078
     *
2079
     * @param bool $recalculate - run it, even if it has run already
2080
     */
2081
    protected function calculateOrderItems($recalculate = false)
2082
    {
2083
        //check if order has modifiers already
2084
        //check /re-add all non-removable ones
2085
        //$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...
2086
        $orderItems = $this->itemsFromDatabase();
2087
        if ($orderItems->count()) {
2088
            foreach ($orderItems as $orderItem) {
2089
                if ($orderItem) {
2090
                    $orderItem->runUpdate($recalculate);
2091
                }
2092
            }
2093
        }
2094
        $this->extend('onCalculateOrderItems', $orderItems);
2095
    }
2096
2097
    /**
2098
     * Calculates and updates all the modifiers.
2099
     *
2100
     * @param bool $recalculate - run it, even if it has run already
2101
     */
2102
    protected function calculateModifiers($recalculate = false)
2103
    {
2104
        $createdModifiers = $this->modifiersFromDatabase();
2105
        if ($createdModifiers->count()) {
2106
            foreach ($createdModifiers as $modifier) {
2107
                if ($modifier) {
2108
                    $modifier->runUpdate($recalculate);
2109
                }
2110
            }
2111
        }
2112
        $this->extend('onCalculateModifiers', $createdModifiers);
2113
    }
2114
2115
    /**
2116
     * Returns the subtotal of the modifiers for this order.
2117
     * If a modifier appears in the excludedModifiers array, it is not counted.
2118
     *
2119
     * @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...
2120
     * @param bool         $stopAtExcludedModifier - when this flag is TRUE, we stop adding the modifiers when we reach an excluded modifier.
2121
     *
2122
     * @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...
2123
     */
2124
    public function ModifiersSubTotal($excluded = null, $stopAtExcludedModifier = false)
2125
    {
2126
        $total = 0;
2127
        $modifiers = $this->Modifiers();
2128
        if ($modifiers->count()) {
2129
            foreach ($modifiers as $modifier) {
2130
                if (!$modifier->IsRemoved()) { //we just double-check this...
2131
                    if (is_array($excluded) && in_array($modifier->ClassName, $excluded)) {
2132
                        if ($stopAtExcludedModifier) {
2133
                            break;
2134
                        }
2135
                        //do the next modifier
2136
                        continue;
2137
                    } elseif (is_string($excluded) && ($modifier->ClassName == $excluded)) {
2138
                        if ($stopAtExcludedModifier) {
2139
                            break;
2140
                        }
2141
                        //do the next modifier
2142
                        continue;
2143
                    }
2144
                    $total += $modifier->CalculationTotal();
2145
                }
2146
            }
2147
        }
2148
2149
        return $total;
2150
    }
2151
2152
    /**
2153
     * returns a modifier that is an instanceof the classname
2154
     * it extends.
2155
     *
2156
     * @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...
2157
     *
2158
     * @return DataObject (OrderModifier)
2159
     **/
2160
    public function RetrieveModifier($className)
2161
    {
2162
        $modifiers = $this->Modifiers();
2163
        if ($modifiers->count()) {
2164
            foreach ($modifiers as $modifier) {
2165
                if (is_a($modifier, Object::getCustomClass($className))) {
2166
                    return $modifier;
2167
                }
2168
            }
2169
        }
2170
    }
2171
2172
/*******************************************************
2173
   * 7. CRUD METHODS (e.g. canView, canEdit, canDelete, etc...)
2174
*******************************************************/
2175
2176
    /**
2177
     * @param Member $member
2178
     *
2179
     * @return DataObject (Member)
2180
     **/
2181
     //TODO: please comment why we make use of this function
2182
    protected function getMemberForCanFunctions(Member $member = null)
2183
    {
2184
        if (!$member) {
2185
            $member = Member::currentUser();
2186
        }
2187
        if (!$member) {
2188
            $member = new Member();
2189
            $member->ID = 0;
2190
        }
2191
2192
        return $member;
2193
    }
2194
2195
    /**
2196
     * @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...
2197
     *
2198
     * @return bool
2199
     **/
2200
    public function canCreate($member = null)
2201
    {
2202
        $member = $this->getMemberForCanFunctions($member);
2203
        $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...
2204
        if ($extended !== null) {
2205
            return $extended;
2206
        }
2207
        if ($member->exists()) {
2208
            return $member->IsShopAdmin();
2209
        }
2210
    }
2211
2212
    /**
2213
     * Standard SS method - can the current member view this order?
2214
     *
2215
     * @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...
2216
     *
2217
     * @return bool
2218
     **/
2219
    public function canView($member = null)
2220
    {
2221
        if (!$this->exists()) {
2222
            return true;
2223
        }
2224
        $member = $this->getMemberForCanFunctions($member);
2225
        //check if this has been "altered" in any DataExtension
2226
        $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...
2227
        if ($extended !== null) {
2228
            return $extended;
2229
        }
2230
        //is the member is a shop admin they can always view it
2231
        if (EcommerceRole::current_member_is_shop_admin($member)) {
2232
            return true;
2233
        }
2234
2235
        //is the member is a shop assistant they can always view it
2236
        if (EcommerceRole::current_member_is_shop_assistant($member)) {
2237
            return true;
2238
        }
2239
        //if the current member OWNS the order, (s)he can always view it.
2240
        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...
2241
            return true;
2242
        }
2243
        //it is the current order
2244
        if ($this->IsInSession()) {
2245
            //we do some additional CHECKS for session hackings!
2246
            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...
2247
                //can't view the order of another member!
2248
                //shop admin exemption is already captured.
2249
                //this is always true
2250
                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...
2251
                    return false;
2252
                }
2253
            } else {
2254
                //order belongs to someone, but current user is NOT logged in...
2255
                //this is allowed!
2256
                //the reason it is allowed is because we want to be able to
2257
                //add order to non-existing member
2258
                return true;
2259
            }
2260
        }
2261
2262
        return false;
2263
    }
2264
2265
    /**
2266
     * @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...
2267
     * @return bool
2268
     */
2269
    public function canOverrideCanView($member = null)
2270
    {
2271
        if ($this->canView($member)) {
2272
            //can view overrides any concerns
2273
            return true;
2274
        } else {
2275
            $tsOrder = strtotime($this->LastEdited);
2276
            $tsNow = time();
2277
            $minutes = EcommerceConfig::get('Order', 'minutes_an_order_can_be_viewed_without_logging_in');
2278
            if ($minutes && ((($tsNow - $tsOrder) / 60) < $minutes)) {
2279
2280
                //has the order been edited recently?
2281
                return true;
2282
            } elseif ($orderStep = $this->MyStep()) {
2283
2284
                // order is being processed ...
2285
                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...
2286
            }
2287
        }
2288
        return false;
2289
    }
2290
2291
    /**
2292
     * @return bool
2293
     */
2294
    public function IsInSession()
2295
    {
2296
        $orderInSession = ShoppingCart::session_order();
2297
2298
        return $orderInSession && $this->ID && $this->ID == $orderInSession->ID;
2299
    }
2300
2301
    /**
2302
     * returns a pseudo random part of the session id.
2303
     *
2304
     * @param int $size
2305
     *
2306
     * @return string
2307
     */
2308
    public function LessSecureSessionID($size = 7, $start = null)
2309
    {
2310
        if (!$start || $start < 0 || $start > (32 - $size)) {
2311
            $start = 0;
2312
        }
2313
2314
        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...
2315
    }
2316
    /**
2317
     *
2318
     * @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...
2319
     *
2320
     * @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...
2321
     **/
2322
    public function canViewAdminStuff($member = null)
2323
    {
2324
        $member = $this->getMemberForCanFunctions($member);
2325
        $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...
2326
        if ($extended !== null) {
2327
            return $extended;
2328
        }
2329
        if (Permission::checkMember($member, Config::inst()->get('EcommerceRole', 'admin_permission_code'))) {
2330
            return true;
2331
        }
2332
    }
2333
2334
    /**
2335
     * if we set canEdit to false then we
2336
     * can not see the child records
2337
     * Basically, you can edit when you can view and canEdit (even as a customer)
2338
     * Or if you are a Shop Admin you can always edit.
2339
     * Otherwise it is false...
2340
     *
2341
     * @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...
2342
     *
2343
     * @return bool
2344
     **/
2345
    public function canEdit($member = null)
2346
    {
2347
        $member = $this->getMemberForCanFunctions($member);
2348
        $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...
2349
        if ($extended !== null) {
2350
            return $extended;
2351
        }
2352
        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...
2353
            return true;
2354
        }
2355
        if (Permission::checkMember($member, Config::inst()->get('EcommerceRole', 'admin_permission_code'))) {
2356
            return true;
2357
        }
2358
        //is the member is a shop assistant they can always view it
2359
        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...
2360
            return true;
2361
        }
2362
        return false;
2363
    }
2364
2365
    /**
2366
     * is the order ready to go through to the
2367
     * checkout process.
2368
     *
2369
     * This method checks all the order items and order modifiers
2370
     * If any of them need immediate attention then this is done
2371
     * first after which it will go through to the checkout page.
2372
     *
2373
     * @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...
2374
     *
2375
     * @return bool
2376
     **/
2377
    public function canCheckout(Member $member = null)
2378
    {
2379
        $member = $this->getMemberForCanFunctions($member);
2380
        $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...
2381
        if ($extended !== null) {
2382
            return $extended;
2383
        }
2384
        $submitErrors = $this->SubmitErrors();
2385
        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...
2386
            return false;
2387
        }
2388
2389
        return true;
2390
    }
2391
2392
    /**
2393
     * Can the order be submitted?
2394
     * this method can be used to stop an order from being submitted
2395
     * due to something not being completed or done.
2396
     *
2397
     * @see Order::SubmitErrors
2398
     *
2399
     * @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...
2400
     *
2401
     * @return bool
2402
     **/
2403
    public function canSubmit(Member $member = null)
2404
    {
2405
        $member = $this->getMemberForCanFunctions($member);
2406
        $extended = $this->extendedCan(__FUNCTION__, $member);
0 ignored issues
show
Documentation introduced by
$member is of type object<DataObject>, but the function expects a object<Member>|integer.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2407
        if ($extended !== null) {
2408
            return $extended;
2409
        }
2410
        if ($this->IsSubmitted()) {
2411
            return false;
2412
        }
2413
        $submitErrors = $this->SubmitErrors();
2414
        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...
2415
            return false;
2416
        }
2417
2418
        return true;
2419
    }
2420
2421
    /**
2422
     * Can a payment be made for this Order?
2423
     *
2424
     * @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...
2425
     *
2426
     * @return bool
2427
     **/
2428
    public function canPay(Member $member = null)
2429
    {
2430
        $member = $this->getMemberForCanFunctions($member);
2431
        $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...
2432
        if ($extended !== null) {
2433
            return $extended;
2434
        }
2435
        if ($this->IsPaid() || $this->IsCancelled() || $this->PaymentIsPending()) {
2436
            return false;
2437
        }
2438
2439
        return $this->MyStep()->CustomerCanPay;
2440
    }
2441
2442
    /**
2443
     * Can the given member cancel this order?
2444
     *
2445
     * @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...
2446
     *
2447
     * @return bool
2448
     **/
2449
    public function canCancel(Member $member = null)
2450
    {
2451
        //if it is already cancelled it can not be cancelled again
2452
        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...
2453
            return false;
2454
        }
2455
        $member = $this->getMemberForCanFunctions($member);
2456
        $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...
2457
        if ($extended !== null) {
2458
            return $extended;
2459
        }
2460
        if (EcommerceRole::current_member_can_process_orders($member)) {
2461
            return true;
2462
        }
2463
2464
        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...
2465
    }
2466
2467
    /**
2468
     * @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...
2469
     *
2470
     * @return bool
2471
     **/
2472
    public function canDelete($member = null)
2473
    {
2474
        $member = $this->getMemberForCanFunctions($member);
2475
        $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...
2476
        if ($extended !== null) {
2477
            return $extended;
2478
        }
2479
        if ($this->IsSubmitted()) {
2480
            return false;
2481
        }
2482
        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...
2483
            return true;
2484
        }
2485
2486
        return false;
2487
    }
2488
2489
    /**
2490
     * Returns all the order logs that the current member can view
2491
     * i.e. some order logs can only be viewed by the admin (e.g. suspected fraud orderlog).
2492
     *
2493
     * @return ArrayList (OrderStatusLogs)
2494
     **/
2495
    public function CanViewOrderStatusLogs()
2496
    {
2497
        $canViewOrderStatusLogs = new ArrayList();
2498
        $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...
2499
        foreach ($logs as $log) {
2500
            if ($log->canView()) {
2501
                $canViewOrderStatusLogs->push($log);
2502
            }
2503
        }
2504
2505
        return $canViewOrderStatusLogs;
2506
    }
2507
2508
    /**
2509
     * returns all the logs that can be viewed by the customer.
2510
     *
2511
     * @return ArrayList (OrderStausLogs)
2512
     */
2513
    public function CustomerViewableOrderStatusLogs()
2514
    {
2515
        $customerViewableOrderStatusLogs = new ArrayList();
2516
        $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...
2517
        if ($logs) {
2518
            foreach ($logs as $log) {
2519
                if (!$log->InternalUseOnly) {
2520
                    $customerViewableOrderStatusLogs->push($log);
2521
                }
2522
            }
2523
        }
2524
2525
        return $customerViewableOrderStatusLogs;
2526
    }
2527
2528
/*******************************************************
2529
   * 8. GET METHODS (e.g. Total, SubTotal, Title, etc...)
2530
*******************************************************/
2531
2532
    /**
2533
     * returns the email to be used for customer communication.
2534
     *
2535
     * @return string
2536
     */
2537
    public function OrderEmail()
2538
    {
2539
        return $this->getOrderEmail();
2540
    }
2541
    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...
2542
    {
2543
        $email = '';
2544
        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...
2545
            $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...
2546
        }
2547
        if (! $email) {
2548
            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...
2549
                $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...
2550
            }
2551
        }
2552
        $extendedEmail = $this->extend('updateOrderEmail', $email);
2553
        if ($extendedEmail !== null && is_array($extendedEmail) && count($extendedEmail)) {
2554
            $email = implode(';', $extendedEmail);
2555
        }
2556
2557
        return $email;
2558
    }
2559
2560
    /**
2561
     * Returns true if there is a prink or email link.
2562
     *
2563
     * @return bool
2564
     */
2565
    public function HasPrintOrEmailLink()
2566
    {
2567
        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...
2568
    }
2569
2570
    /**
2571
     * returns the absolute link to the order that can be used in the customer communication (email).
2572
     *
2573
     * @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...
2574
     */
2575
    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...
2576
    {
2577
        return $this->getEmailLink();
2578
    }
2579
    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...
2580
    {
2581
        if (!isset($_REQUEST['print'])) {
2582
            if ($this->IsSubmitted()) {
2583
                return Director::AbsoluteURL(OrderConfirmationPage::get_email_link($this->ID, $this->MyStep()->getEmailClassName(), $actuallySendEmail = true));
2584
            }
2585
        }
2586
    }
2587
2588
    /**
2589
     * returns the absolute link to the order for printing.
2590
     *
2591
     * @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...
2592
     */
2593
    public function PrintLink()
2594
    {
2595
        return $this->getPrintLink();
2596
    }
2597
    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...
2598
    {
2599
        if (!isset($_REQUEST['print'])) {
2600
            if ($this->IsSubmitted()) {
2601
                return Director::AbsoluteURL(OrderConfirmationPage::get_order_link($this->ID)).'?print=1';
2602
            }
2603
        }
2604
    }
2605
2606
    /**
2607
     * returns the absolute link to the order for printing.
2608
     *
2609
     * @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...
2610
     */
2611
    public function PackingSlipLink()
2612
    {
2613
        return $this->getPackingSlipLink();
2614
    }
2615
    public function getPackingSlipLink()
2616
    {
2617
        if ($this->IsSubmitted()) {
2618
            return Director::AbsoluteURL(OrderConfirmationPage::get_order_link($this->ID)).'?packingslip=1';
2619
        }
2620
    }
2621
2622
    /**
2623
     * returns the absolute link that the customer can use to retrieve the email WITHOUT logging in.
2624
     *
2625
     * @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...
2626
     */
2627
    public function RetrieveLink()
2628
    {
2629
        return $this->getRetrieveLink();
2630
    }
2631
2632
    public function getRetrieveLink()
2633
    {
2634
        //important to recalculate!
2635
        if ($this->IsSubmitted($recalculate = true)) {
2636
            //add session ID if not added yet...
2637
            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...
2638
                $this->write();
2639
            }
2640
2641
            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...
2642
        } else {
2643
            return Director::AbsoluteURL('/shoppingcart/loadorder/'.$this->ID.'/');
2644
        }
2645
    }
2646
2647
    public function ShareLink()
2648
    {
2649
        return $this->getShareLink();
2650
    }
2651
2652
    public function getShareLink()
2653
    {
2654
        $orderItems = $this->itemsFromDatabase();
2655
        $action = 'share';
2656
        $array = array();
2657
        foreach ($orderItems as $orderItem) {
2658
            $array[] = implode(
2659
                ',',
2660
                array(
2661
                    $orderItem->BuyableClassName,
2662
                    $orderItem->BuyableID,
2663
                    $orderItem->Quantity
2664
                )
2665
            );
2666
        }
2667
2668
        return Director::AbsoluteURL(CartPage::find_link($action.'/'.implode('-', $array)));
2669
    }
2670
2671
    /**
2672
     * @alias for getFeedbackLink
2673
     * @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...
2674
     */
2675
    public function FeedbackLink()
2676
    {
2677
        return $this->getFeedbackLink();
2678
    }
2679
2680
    /**
2681
     * @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...
2682
     */
2683
    public function getFeedbackLink()
2684
    {
2685
        $orderConfirmationPage = OrderConfirmationPage::get()->first();
2686
        if($orderConfirmationPage->IsFeedbackEnabled) {
2687
2688
            return Director::AbsoluteURL($orderConfirmationPage->Link('feedback'));
2689
        }
2690
    }
2691
2692
    /**
2693
     * link to delete order.
2694
     *
2695
     * @return string
2696
     */
2697
    public function DeleteLink()
2698
    {
2699
        return $this->getDeleteLink();
2700
    }
2701
    public function getDeleteLink()
2702
    {
2703
        if ($this->canDelete()) {
2704
            return ShoppingCart_Controller::delete_order_link($this->ID);
2705
        } else {
2706
            return '';
2707
        }
2708
    }
2709
2710
    /**
2711
     * link to copy order.
2712
     *
2713
     * @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...
2714
     */
2715
    public function CopyOrderLink()
2716
    {
2717
        return $this->getCopyOrderLink();
2718
    }
2719
    public function getCopyOrderLink()
2720
    {
2721
        if ($this->canView() && $this->IsSubmitted()) {
2722
            return ShoppingCart_Controller::copy_order_link($this->ID);
2723
        } else {
2724
            return '';
2725
        }
2726
    }
2727
2728
    /**
2729
     * A "Title" for the order, which summarises the main details (date, and customer) in a string.
2730
     *
2731
     * @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...
2732
     * @param bool   $includeName - e.g. by Mr Johnson
2733
     *
2734
     * @return string
2735
     **/
2736
    public function Title($dateFormat = null, $includeName = false)
2737
    {
2738
        return $this->getTitle($dateFormat, $includeName);
2739
    }
2740
    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...
2741
    {
2742
        if ($this->exists()) {
2743
            if ($dateFormat === null) {
2744
                $dateFormat = EcommerceConfig::get('Order', 'date_format_for_title');
2745
            }
2746
            if ($includeName === null) {
2747
                $includeName = EcommerceConfig::get('Order', 'include_customer_name_in_title');
2748
            }
2749
            $title = $this->i18n_singular_name()." #".number_format($this->ID);
2750
            if ($dateFormat) {
2751
                if ($submissionLog = $this->SubmissionLog()) {
2752
                    $dateObject = $submissionLog->dbObject('Created');
2753
                    $placed = _t('Order.PLACED', 'placed');
2754
                } else {
2755
                    $dateObject = $this->dbObject('Created');
2756
                    $placed = _t('Order.STARTED', 'started');
2757
                }
2758
                $title .= ' - '.$placed.' '.$dateObject->Format($dateFormat);
2759
            }
2760
            $name = '';
2761
            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...
2762
                $name = ' - '._t('Order.CANCELLED', 'CANCELLED');
2763
            }
2764
            if ($includeName) {
2765
                $by = _t('Order.BY', 'by');
2766
                if (!$name) {
2767
                    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...
2768
                        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...
2769
                            $name = ' - '.$by.' '.$billingAddress->Prefix.' '.$billingAddress->FirstName.' '.$billingAddress->Surname;
2770
                        }
2771
                    }
2772
                }
2773
                if (!$name) {
2774
                    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...
2775
                        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...
2776
                            if ($member->exists()) {
2777
                                if ($memberName = $member->getName()) {
2778
                                    if (!trim($memberName)) {
2779
                                        $memberName = _t('Order.ANONYMOUS', 'anonymous');
2780
                                    }
2781
                                    $name = ' - '.$by.' '.$memberName;
2782
                                }
2783
                            }
2784
                        }
2785
                    }
2786
                }
2787
            }
2788
            $title .= $name;
2789
        } else {
2790
            $title = _t('Order.NEW', 'New').' '.$this->i18n_singular_name();
2791
        }
2792
        $extendedTitle = $this->extend('updateTitle', $title);
2793
        if ($extendedTitle !== null && is_array($extendedTitle) && count($extendedTitle)) {
2794
            $title = implode('; ', $extendedTitle);
2795
        }
2796
2797
        return $title;
2798
    }
2799
2800
    /**
2801
     * Returns the subtotal of the items for this order.
2802
     *
2803
     * @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...
2804
     */
2805
    public function SubTotal()
2806
    {
2807
        return $this->getSubTotal();
2808
    }
2809
    public function getSubTotal()
2810
    {
2811
        $result = 0;
2812
        $items = $this->Items();
2813
        if ($items->count()) {
2814
            foreach ($items as $item) {
2815
                if (is_a($item, Object::getCustomClass('OrderAttribute'))) {
2816
                    $result += $item->Total();
2817
                }
2818
            }
2819
        }
2820
2821
        return $result;
2822
    }
2823
2824
    /**
2825
     * @return Currency (DB Object)
2826
     **/
2827
    public function SubTotalAsCurrencyObject()
2828
    {
2829
        return DBField::create_field('Currency', $this->SubTotal());
2830
    }
2831
2832
    /**
2833
     * @return Money
2834
     **/
2835
    public function SubTotalAsMoney()
2836
    {
2837
        return $this->getSubTotalAsMoney();
2838
    }
2839
    public function getSubTotalAsMoney()
2840
    {
2841
        return EcommerceCurrency::get_money_object_from_order_currency($this->SubTotal(), $this);
2842
    }
2843
2844
    /**
2845
     * @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...
2846
     * @param bool         $stopAtExcludedModifier - when this flag is TRUE, we stop adding the modifiers when we reach an excluded modifier.
2847
     *
2848
     * @return Currency (DB Object)
2849
     **/
2850
    public function ModifiersSubTotalAsCurrencyObject($excluded = null, $stopAtExcludedModifier = false)
2851
    {
2852
        return DBField::create_field('Currency', $this->ModifiersSubTotal($excluded, $stopAtExcludedModifier));
2853
    }
2854
2855
    /**
2856
     * @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...
2857
     * @param bool         $stopAtExcludedModifier - when this flag is TRUE, we stop adding the modifiers when we reach an excluded modifier.
2858
     *
2859
     * @return Money (DB Object)
2860
     **/
2861
    public function ModifiersSubTotalAsMoneyObject($excluded = null, $stopAtExcludedModifier = false)
2862
    {
2863
        return EcommerceCurrency::get_money_object_from_order_currency($this->ModifiersSubTotal($excluded, $stopAtExcludedModifier), $this);
2864
    }
2865
2866
    /**
2867
     * Returns the total cost of an order including the additional charges or deductions of its modifiers.
2868
     *
2869
     * @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...
2870
     */
2871
    public function Total()
2872
    {
2873
        return $this->getTotal();
2874
    }
2875
    public function getTotal()
2876
    {
2877
        return $this->SubTotal() + $this->ModifiersSubTotal();
2878
    }
2879
2880
    /**
2881
     * @return Currency (DB Object)
2882
     **/
2883
    public function TotalAsCurrencyObject()
2884
    {
2885
        return DBField::create_field('Currency', $this->Total());
2886
    }
2887
2888
    /**
2889
     * @return Money
2890
     **/
2891
    public function TotalAsMoney()
2892
    {
2893
        return $this->getTotalAsMoney();
2894
    }
2895
    public function getTotalAsMoney()
2896
    {
2897
        return EcommerceCurrency::get_money_object_from_order_currency($this->Total(), $this);
2898
    }
2899
2900
    /**
2901
     * Checks to see if any payments have been made on this order
2902
     * and if so, subracts the payment amount from the order.
2903
     *
2904
     * @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...
2905
     **/
2906
    public function TotalOutstanding()
2907
    {
2908
        return $this->getTotalOutstanding();
2909
    }
2910
    public function getTotalOutstanding()
2911
    {
2912
        if ($this->IsSubmitted()) {
2913
            $total = $this->Total();
2914
            $paid = $this->TotalPaid();
2915
            $outstanding = $total - $paid;
2916
            $maxDifference = EcommerceConfig::get('Order', 'maximum_ignorable_sales_payments_difference');
2917
            if (abs($outstanding) < $maxDifference) {
2918
                $outstanding = 0;
2919
            }
2920
2921
            return floatval($outstanding);
2922
        } else {
2923
            return 0;
2924
        }
2925
    }
2926
2927
    /**
2928
     * @return Currency (DB Object)
2929
     **/
2930
    public function TotalOutstandingAsCurrencyObject()
2931
    {
2932
        return DBField::create_field('Currency', $this->TotalOutstanding());
2933
    }
2934
2935
    /**
2936
     * @return Money
2937
     **/
2938
    public function TotalOutstandingAsMoney()
2939
    {
2940
        return $this->getTotalOutstandingAsMoney();
2941
    }
2942
    public function getTotalOutstandingAsMoney()
2943
    {
2944
        return EcommerceCurrency::get_money_object_from_order_currency($this->TotalOutstanding(), $this);
2945
    }
2946
2947
    /**
2948
     * @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...
2949
     */
2950
    public function TotalPaid()
2951
    {
2952
        return $this->getTotalPaid();
2953
    }
2954
    public function getTotalPaid()
2955
    {
2956
        $paid = 0;
2957
        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...
2958
            foreach ($payments as $payment) {
2959
                if ($payment->Status == 'Success') {
2960
                    $paid += $payment->Amount->getAmount();
2961
                }
2962
            }
2963
        }
2964
        $reverseExchange = 1;
2965
        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...
2966
            $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...
2967
        }
2968
2969
        return $paid * $reverseExchange;
2970
    }
2971
2972
    /**
2973
     * @return Currency (DB Object)
2974
     **/
2975
    public function TotalPaidAsCurrencyObject()
2976
    {
2977
        return DBField::create_field('Currency', $this->TotalPaid());
2978
    }
2979
2980
    /**
2981
     * @return Money
2982
     **/
2983
    public function TotalPaidAsMoney()
2984
    {
2985
        return $this->getTotalPaidAsMoney();
2986
    }
2987
    public function getTotalPaidAsMoney()
2988
    {
2989
        return EcommerceCurrency::get_money_object_from_order_currency($this->TotalPaid(), $this);
2990
    }
2991
2992
    /**
2993
     * returns the total number of OrderItems (not modifiers).
2994
     * This is meant to run as fast as possible to quickly check
2995
     * if there is anything in the cart.
2996
     *
2997
     * @param bool $recalculate - do we need to recalculate (value is retained during lifetime of Object)
2998
     *
2999
     * @return int
3000
     **/
3001
    public function TotalItems($recalculate = false)
3002
    {
3003
        return $this->getTotalItems($recalculate);
3004
    }
3005
    public function getTotalItems($recalculate = false)
3006
    {
3007
        if ($this->totalItems === null || $recalculate) {
3008
            $this->totalItems = OrderItem::get()
3009
                ->where('"OrderAttribute"."OrderID" = '.$this->ID.' AND "OrderItem"."Quantity" > 0')
3010
                ->count();
3011
        }
3012
3013
        return $this->totalItems;
3014
    }
3015
3016
    /**
3017
     * Little shorthand.
3018
     *
3019
     * @param bool $recalculate
3020
     *
3021
     * @return bool
3022
     **/
3023
    public function MoreThanOneItemInCart($recalculate = false)
3024
    {
3025
        return $this->TotalItems($recalculate) > 1 ? true : false;
3026
    }
3027
3028
    /**
3029
     * returns the total number of OrderItems (not modifiers) times their respectective quantities.
3030
     *
3031
     * @param bool $recalculate - force recalculation
3032
     *
3033
     * @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...
3034
     **/
3035
    public function TotalItemsTimesQuantity($recalculate = false)
3036
    {
3037
        return $this->getTotalItemsTimesQuantity($recalculate);
3038
    }
3039
    public function getTotalItemsTimesQuantity($recalculate = false)
3040
    {
3041
        if ($this->totalItemsTimesQuantity === null || $recalculate) {
3042
            //to do, why do we check if you can edit ????
3043
            $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...
3044
                SELECT SUM("OrderItem"."Quantity")
3045
                FROM "OrderItem"
3046
                    INNER JOIN "OrderAttribute" ON "OrderAttribute"."ID" = "OrderItem"."ID"
3047
                WHERE
3048
                    "OrderAttribute"."OrderID" = '.$this->ID.'
3049
                    AND "OrderItem"."Quantity" > 0'
3050
            )->value();
3051
        }
3052
3053
        return $this->totalItemsTimesQuantity - 0;
3054
    }
3055
3056
    /**
3057
     *
3058
     * @return string (country code)
3059
     **/
3060
    public function Country()
3061
    {
3062
        return $this->getCountry();
3063
    }
3064
3065
    /**
3066
    * Returns the country code for the country that applies to the order.
3067
    * @alias  for getCountry
3068
    *
3069
    * @return string - country code e.g. NZ
3070
     */
3071
    public function getCountry()
3072
    {
3073
        $countryCodes = array(
3074
            'Billing' => '',
3075
            'Shipping' => '',
3076
        );
3077
        $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...
3078
        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...
3079
            $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...
3080
            if ($billingAddress) {
3081
                if ($billingAddress->Country) {
3082
                    $countryCodes['Billing'] = $billingAddress->Country;
3083
                }
3084
            }
3085
        }
3086
        if ($this->ShippingAddressID && $this->UseShippingAddress) {
0 ignored issues
show
Documentation introduced by
The property ShippingAddressID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

Loading history...
Documentation introduced by
The property UseShippingAddress does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

Loading history...
3087
            $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...
3088
            if ($shippingAddress) {
3089
                if ($shippingAddress->ShippingCountry) {
3090
                    $countryCodes['Shipping'] = $shippingAddress->ShippingCountry;
3091
                }
3092
            }
3093
        }
3094
        if (
3095
            (EcommerceConfig::get('OrderAddress', 'use_shipping_address_for_main_region_and_country') && $countryCodes['Shipping'])
3096
            ||
3097
            (!$countryCodes['Billing'] && $countryCodes['Shipping'])
3098
        ) {
3099
            $code = $countryCodes['Shipping'];
3100
        } elseif ($countryCodes['Billing']) {
3101
            $code = $countryCodes['Billing'];
3102
        } else {
3103
            $code = EcommerceCountry::get_country_from_ip();
3104
        }
3105
3106
        return $code;
3107
    }
3108
3109
    /**
3110
     * @alias for getFullNameCountry
3111
     *
3112
     * @return string - country name
3113
     **/
3114
    public function FullNameCountry()
3115
    {
3116
        return $this->getFullNameCountry();
3117
    }
3118
3119
    /**
3120
     * returns name of coutry.
3121
     *
3122
     * @return string - country name
3123
     **/
3124
    public function getFullNameCountry()
3125
    {
3126
        return EcommerceCountry::find_title($this->Country());
3127
    }
3128
3129
    /**
3130
     * @alis for getExpectedCountryName
3131
     * @return string - country name
3132
     **/
3133
    public function ExpectedCountryName()
3134
    {
3135
        return $this->getExpectedCountryName();
3136
    }
3137
3138
    /**
3139
     * returns name of coutry that we expect the customer to have
3140
     * This takes into consideration more than just what has been entered
3141
     * for example, it looks at GEO IP.
3142
     *
3143
     * @todo: why do we dont return a string IF there is only one item.
3144
     *
3145
     * @return string - country name
3146
     **/
3147
    public function getExpectedCountryName()
3148
    {
3149
        return EcommerceCountry::find_title(EcommerceCountry::get_country(false, $this->ID));
3150
    }
3151
3152
    /**
3153
     * return the title of the fixed country (if any).
3154
     *
3155
     * @return string | empty string
3156
     **/
3157
    public function FixedCountry()
3158
    {
3159
        return $this->getFixedCountry();
3160
    }
3161
    public function getFixedCountry()
3162
    {
3163
        $code = EcommerceCountry::get_fixed_country_code();
3164
        if ($code) {
3165
            return EcommerceCountry::find_title($code);
3166
        }
3167
3168
        return '';
3169
    }
3170
3171
    /**
3172
     * Returns the region that applies to the order.
3173
     * we check both billing and shipping, in case one of them is empty.
3174
     *
3175
     * @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...
3176
     **/
3177
    public function Region()
3178
    {
3179
        return $this->getRegion();
3180
    }
3181
    public function getRegion()
3182
    {
3183
        $regionIDs = array(
3184
            'Billing' => 0,
3185
            'Shipping' => 0,
3186
        );
3187
        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...
3188
            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...
3189
                if ($billingAddress->RegionID) {
3190
                    $regionIDs['Billing'] = $billingAddress->RegionID;
3191
                }
3192
            }
3193
        }
3194
        if ($this->CanHaveShippingAddress()) {
3195
            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...
3196
                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...
3197
                    if ($shippingAddress->ShippingRegionID) {
3198
                        $regionIDs['Shipping'] = $shippingAddress->ShippingRegionID;
3199
                    }
3200
                }
3201
            }
3202
        }
3203
        if (count($regionIDs)) {
3204
            //note the double-check with $this->CanHaveShippingAddress() and get_use_....
3205
            if ($this->CanHaveShippingAddress() && EcommerceConfig::get('OrderAddress', 'use_shipping_address_for_main_region_and_country') && $regionIDs['Shipping']) {
3206
                return EcommerceRegion::get()->byID($regionIDs['Shipping']);
3207
            } else {
3208
                return EcommerceRegion::get()->byID($regionIDs['Billing']);
3209
            }
3210
        } else {
3211
            return EcommerceRegion::get()->byID(EcommerceRegion::get_region_from_ip());
3212
        }
3213
    }
3214
3215
    /**
3216
     * Casted variable
3217
     * Currency is not the same as the standard one?
3218
     *
3219
     * @return bool
3220
     **/
3221
    public function HasAlternativeCurrency()
3222
    {
3223
        return $this->getHasAlternativeCurrency();
3224
    }
3225
    public function getHasAlternativeCurrency()
3226
    {
3227
        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...
3228
            if ($currency->IsDefault()) {
0 ignored issues
show
Coding Style introduced by
The if-else statement can be simplified to return !$currency->IsDefault();.
Loading history...
3229
                return false;
3230
            } else {
3231
                return true;
3232
            }
3233
        } else {
3234
            return false;
3235
        }
3236
    }
3237
3238
    /**
3239
     * Makes sure exchange rate is updated and maintained before order is submitted
3240
     * This method is public because it could be called from a shopping Cart Object.
3241
     **/
3242
    public function EnsureCorrectExchangeRate()
3243
    {
3244
        if (!$this->IsSubmitted()) {
3245
            $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...
3246
            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...
3247
                if ($currency->IsDefault()) {
3248
                    $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...
3249
                } else {
3250
                    $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...
3251
                }
3252
            } else {
3253
                $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...
3254
            }
3255
            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...
3256
                $this->write();
3257
            }
3258
        }
3259
    }
3260
3261
    /**
3262
     * speeds up processing by storing the IsSubmitted value
3263
     * we start with -1 to know if it has been requested before.
3264
     *
3265
     * @var bool
3266
     */
3267
    protected $_isSubmittedTempVar = -1;
3268
3269
    /**
3270
     * Casted variable - has the order been submitted?
3271
     * alias
3272
     * @param bool $recalculate
3273
     *
3274
     * @return bool
3275
     **/
3276
    public function IsSubmitted($recalculate = true)
3277
    {
3278
        return $this->getIsSubmitted($recalculate);
3279
    }
3280
3281
    /**
3282
     * Casted variable - has the order been submitted?
3283
     *
3284
     * @param bool $recalculate
3285
     *
3286
     * @return bool
3287
     **/
3288
    public function getIsSubmitted($recalculate = false)
3289
    {
3290
        if ($this->_isSubmittedTempVar === -1 || $recalculate) {
3291
            if ($this->SubmissionLog()) {
3292
                $this->_isSubmittedTempVar = true;
3293
            } else {
3294
                $this->_isSubmittedTempVar = false;
3295
            }
3296
        }
3297
3298
        return $this->_isSubmittedTempVar;
3299
    }
3300
3301
    /**
3302
     *
3303
     *
3304
     * @return bool
3305
     */
3306
    public function IsArchived()
3307
    {
3308
        $lastStep = OrderStep::get()->Last();
3309
        if ($lastStep) {
3310
            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...
3311
                return true;
3312
            }
3313
        }
3314
        return false;
3315
    }
3316
3317
    /**
3318
     * Submission Log for this Order (if any).
3319
     *
3320
     * @return Submission Log (OrderStatusLog_Submitted) | Null
3321
     **/
3322
    public function SubmissionLog()
3323
    {
3324
        $className = EcommerceConfig::get('OrderStatusLog', 'order_status_log_class_used_for_submitting_order');
3325
3326
        return $className::get()
3327
            ->Filter(array('OrderID' => $this->ID))
3328
            ->Last();
3329
    }
3330
3331
    /**
3332
     * @return int
3333
     */
3334
    public function SecondsSinceBeingSubmitted()
3335
    {
3336
        if ($submissionLog = $this->SubmissionLog()) {
3337
            return time() - strtotime($submissionLog->Created);
3338
        } else {
3339
            return 0;
3340
        }
3341
    }
3342
3343
    /**
3344
     * if the order can not be submitted,
3345
     * then the reasons why it can not be submitted
3346
     * will be returned by this method.
3347
     *
3348
     * @see Order::canSubmit
3349
     *
3350
     * @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...
3351
     */
3352
    public function SubmitErrors()
3353
    {
3354
        $al = null;
3355
        $extendedSubmitErrors = $this->extend('updateSubmitErrors');
3356
        if ($extendedSubmitErrors !== null && is_array($extendedSubmitErrors) && count($extendedSubmitErrors)) {
3357
            $al = ArrayList::create();
3358
            foreach ($extendedSubmitErrors as $returnResultArray) {
3359
                foreach ($returnResultArray as $issue) {
3360
                    if ($issue) {
3361
                        $al->push(ArrayData::create(array("Title" => $issue)));
3362
                    }
3363
                }
3364
            }
3365
        }
3366
        return $al;
3367
    }
3368
3369
    /**
3370
     * Casted variable - has the order been submitted?
3371
     *
3372
     * @param bool $withDetail
3373
     *
3374
     * @return string
3375
     **/
3376
    public function CustomerStatus($withDetail = true)
3377
    {
3378
        return $this->getCustomerStatus($withDetail);
3379
    }
3380
    public function getCustomerStatus($withDetail = true)
3381
    {
3382
        $str = '';
3383
        if ($this->MyStep()->ShowAsUncompletedOrder) {
3384
            $str = _t('Order.UNCOMPLETED', 'Uncompleted');
3385
        } elseif ($this->MyStep()->ShowAsInProcessOrder) {
3386
            $str = _t('Order.IN_PROCESS', 'In Process');
3387
        } elseif ($this->MyStep()->ShowAsCompletedOrder) {
3388
            $str = _t('Order.COMPLETED', 'Completed');
3389
        }
3390
        if ($withDetail) {
3391
            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...
3392
                $str .= ' ('.$this->MyStep()->Name.')';
3393
            }
3394
        }
3395
3396
        return $str;
3397
    }
3398
3399
    /**
3400
     * Casted variable - does the order have a potential shipping address?
3401
     *
3402
     * @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...
3403
     **/
3404
    public function CanHaveShippingAddress()
3405
    {
3406
        return $this->getCanHaveShippingAddress();
3407
    }
3408
    public function getCanHaveShippingAddress()
3409
    {
3410
        return EcommerceConfig::get('OrderAddress', 'use_separate_shipping_address');
3411
    }
3412
3413
    /**
3414
     * returns the link to view the Order
3415
     * WHY NOT CHECKOUT PAGE: first we check for cart page.
3416
     *
3417
     * @return CartPage | Null
3418
     */
3419
    public function DisplayPage()
3420
    {
3421
        if ($this->MyStep() && $this->MyStep()->AlternativeDisplayPage()) {
3422
            $page = $this->MyStep()->AlternativeDisplayPage();
3423
        } elseif ($this->IsSubmitted()) {
3424
            $page = OrderConfirmationPage::get()->First();
3425
        } else {
3426
            $page = CartPage::get()
3427
                ->Filter(array('ClassName' => 'CartPage'))
3428
                ->First();
3429
            if (!$page) {
3430
                $page = CheckoutPage::get()->First();
3431
            }
3432
        }
3433
3434
        return $page;
3435
    }
3436
3437
    /**
3438
     * returns the link to view the Order
3439
     * WHY NOT CHECKOUT PAGE: first we check for cart page.
3440
     * If a cart page has been created then we refer through to Cart Page.
3441
     * Otherwise it will default to the checkout page.
3442
     *
3443
     * @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...
3444
     *
3445
     * @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...
3446
     */
3447
    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...
3448
    {
3449
        $page = $this->DisplayPage();
3450
        if ($page) {
3451
            return $page->getOrderLink($this->ID, $action);
3452
        } else {
3453
            user_error('A Cart / Checkout Page + an Order Confirmation Page needs to be setup for the e-commerce module to work.', E_USER_NOTICE);
3454
            $page = ErrorPage::get()
3455
                ->Filter(array('ErrorCode' => '404'))
3456
                ->First();
3457
            if ($page) {
3458
                return $page->Link();
3459
            }
3460
        }
3461
    }
3462
3463
    /**
3464
     * Returns to link to access the Order's API.
3465
     *
3466
     * @param string $version
3467
     * @param string $extension
3468
     *
3469
     * @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...
3470
     */
3471
    public function APILink($version = 'v1', $extension = 'xml')
3472
    {
3473
        return Director::AbsoluteURL("/api/ecommerce/$version/Order/".$this->ID."/.$extension");
3474
    }
3475
3476
    /**
3477
     * returns the link to finalise the Order.
3478
     *
3479
     * @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...
3480
     */
3481
    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...
3482
    {
3483
        $page = CheckoutPage::get()->First();
3484
        if ($page) {
3485
            return $page->Link();
3486
        } else {
3487
            $page = ErrorPage::get()
3488
                ->Filter(array('ErrorCode' => '404'))
3489
                ->First();
3490
            if ($page) {
3491
                return $page->Link();
3492
            }
3493
        }
3494
    }
3495
3496
    /**
3497
     * Converts the Order into HTML, based on the Order Template.
3498
     *
3499
     * @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...
3500
     **/
3501
    public function ConvertToHTML()
3502
    {
3503
        Config::nest();
3504
        Config::inst()->update('SSViewer', 'theme_enabled', true);
3505
        $html = $this->renderWith('Order');
3506
        Config::unnest();
3507
        $html = preg_replace('/(\s)+/', ' ', $html);
3508
3509
        return DBField::create_field('HTMLText', $html);
3510
    }
3511
3512
    /**
3513
     * Converts the Order into a serialized string
3514
     * TO DO: check if this works and check if we need to use special sapphire serialization code.
3515
     *
3516
     * @return string - serialized object
3517
     **/
3518
    public function ConvertToString()
3519
    {
3520
        return serialize($this->addHasOneAndHasManyAsVariables());
3521
    }
3522
3523
    /**
3524
     * Converts the Order into a JSON object
3525
     * TO DO: check if this works and check if we need to use special sapphire JSON code.
3526
     *
3527
     * @return string -  JSON
3528
     **/
3529
    public function ConvertToJSON()
3530
    {
3531
        return json_encode($this->addHasOneAndHasManyAsVariables());
3532
    }
3533
3534
    /**
3535
     * returns itself wtih more data added as variables.
3536
     * We add has_one and has_many as variables like this: $this->MyHasOne_serialized = serialize($this->MyHasOne()).
3537
     *
3538
     * @return Order - with most important has one and has many items included as variables.
3539
     **/
3540
    protected function addHasOneAndHasManyAsVariables()
3541
    {
3542
        $object = clone $this;
3543
        $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...
3544
        $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...
3545
        $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...
3546
        $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...
3547
        $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...
3548
        $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...
3549
        $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...
3550
3551
        return $object;
3552
    }
3553
3554
/*******************************************************
3555
   * 9. TEMPLATE RELATED STUFF
3556
*******************************************************/
3557
3558
    /**
3559
     * returns the instance of EcommerceConfigAjax for use in templates.
3560
     * In templates, it is used like this:
3561
     * $EcommerceConfigAjax.TableID.
3562
     *
3563
     * @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...
3564
     **/
3565
    public function AJAXDefinitions()
3566
    {
3567
        return EcommerceConfigAjax::get_one($this);
3568
    }
3569
3570
    /**
3571
     * returns the instance of EcommerceDBConfig.
3572
     *
3573
     * @return EcommerceDBConfig
3574
     **/
3575
    public function EcomConfig()
3576
    {
3577
        return EcommerceDBConfig::current_ecommerce_db_config();
3578
    }
3579
3580
    /**
3581
     * Collects the JSON data for an ajax return of the cart.
3582
     *
3583
     * @param array $js
3584
     *
3585
     * @return array (for use in AJAX for JSON)
3586
     **/
3587
    public function updateForAjax(array $js)
3588
    {
3589
        $function = EcommerceConfig::get('Order', 'ajax_subtotal_format');
3590
        if (is_array($function)) {
3591
            list($function, $format) = $function;
3592
        }
3593
        $subTotal = $this->$function();
3594
        if (isset($format)) {
3595
            $subTotal = $subTotal->$format();
3596
            unset($format);
3597
        }
3598
        $function = EcommerceConfig::get('Order', 'ajax_total_format');
3599
        if (is_array($function)) {
3600
            list($function, $format) = $function;
3601
        }
3602
        $total = $this->$function();
3603
        if (isset($format)) {
3604
            $total = $total->$format();
3605
        }
3606
        $ajaxObject = $this->AJAXDefinitions();
3607
        $js[] = array(
3608
            't' => 'id',
3609
            's' => $ajaxObject->TableSubTotalID(),
3610
            'p' => 'innerHTML',
3611
            'v' => $subTotal,
3612
        );
3613
        $js[] = array(
3614
            't' => 'id',
3615
            's' => $ajaxObject->TableTotalID(),
3616
            'p' => 'innerHTML',
3617
            'v' => $total,
3618
        );
3619
        $js[] = array(
3620
            't' => 'class',
3621
            's' => $ajaxObject->TotalItemsClassName(),
3622
            'p' => 'innerHTML',
3623
            'v' => $this->TotalItems($recalculate = true),
3624
        );
3625
        $js[] = array(
3626
            't' => 'class',
3627
            's' => $ajaxObject->TotalItemsTimesQuantityClassName(),
3628
            'p' => 'innerHTML',
3629
            'v' => $this->TotalItemsTimesQuantity(),
3630
        );
3631
        $js[] = array(
3632
            't' => 'class',
3633
            's' => $ajaxObject->ExpectedCountryClassName(),
3634
            'p' => 'innerHTML',
3635
            'v' => $this->ExpectedCountryName(),
3636
        );
3637
3638
        return $js;
3639
    }
3640
3641
    /**
3642
     * @ToDO: move to more appropriate class
3643
     *
3644
     * @return float
3645
     **/
3646
    public function SubTotalCartValue()
3647
    {
3648
        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...
3649
    }
3650
3651
/*******************************************************
3652
   * 10. STANDARD SS METHODS (requireDefaultRecords, onBeforeDelete, etc...)
3653
*******************************************************/
3654
3655
    /**
3656
     *standard SS method.
3657
     **/
3658
    public function populateDefaults()
3659
    {
3660
        parent::populateDefaults();
3661
    }
3662
3663
    public function onBeforeWrite()
3664
    {
3665
        parent::onBeforeWrite();
3666
        if (! $this->getCanHaveShippingAddress()) {
3667
            $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...
3668
        }
3669
        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...
3670
            $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...
3671
        }
3672
        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...
3673
            $generator = Injector::inst()->create('RandomGenerator');
3674
            $token = $generator->randomToken('sha1');
3675
            $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...
3676
        }
3677
    }
3678
3679
    /**
3680
     * standard SS method
3681
     * adds the ability to update order after writing it.
3682
     **/
3683
    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...
3684
    {
3685
        parent::onAfterWrite();
3686
        //crucial!
3687
        self::set_needs_recalculating(true, $this->ID);
3688
        // quick double-check
3689
        if ($this->IsCancelled() && ! $this->IsArchived()) {
3690
            $this->Archive($avoidWrites = true);
3691
        }
3692
        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...
3693
            //do nothing
3694
        } else {
3695
            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...
3696
                $this->calculateOrderAttributes($recalculate = false);
3697
                if (EcommerceRole::current_member_is_shop_admin()) {
3698
                    if (isset($_REQUEST['SubmitOrderViaCMS'])) {
3699
                        $this->tryToFinaliseOrder();
3700
                        //just in case it writes again...
3701
                        unset($_REQUEST['SubmitOrderViaCMS']);
3702
                    }
3703
                }
3704
            }
3705
        }
3706
    }
3707
3708
    /**
3709
     *standard SS method.
3710
     *
3711
     * delete attributes, statuslogs, and payments
3712
     * THIS SHOULD NOT BE USED AS ORDERS SHOULD BE CANCELLED NOT DELETED
3713
     */
3714
    public function onBeforeDelete()
3715
    {
3716
        parent::onBeforeDelete();
3717
        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...
3718
            foreach ($attributes as $attribute) {
3719
                $attribute->delete();
3720
                $attribute->destroy();
3721
            }
3722
        }
3723
3724
        //THE REST WAS GIVING ERRORS - POSSIBLY DUE TO THE FUNNY RELATIONSHIP (one-one, two times...)
3725
        /*
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...
3726
        if($billingAddress = $this->BillingAddress()) {
3727
            if($billingAddress->exists()) {
3728
                $billingAddress->delete();
3729
                $billingAddress->destroy();
3730
            }
3731
        }
3732
        if($shippingAddress = $this->ShippingAddress()) {
3733
            if($shippingAddress->exists()) {
3734
                $shippingAddress->delete();
3735
                $shippingAddress->destroy();
3736
            }
3737
        }
3738
3739
        if($statuslogs = $this->OrderStatusLogs()){
3740
            foreach($statuslogs as $log){
3741
                $log->delete();
3742
                $log->destroy();
3743
            }
3744
        }
3745
        if($payments = $this->Payments()){
3746
            foreach($payments as $payment){
3747
                $payment->delete();
3748
                $payment->destroy();
3749
            }
3750
        }
3751
        if($emails = $this->Emails()) {
3752
            foreach($emails as $email){
3753
                $email->delete();
3754
                $email->destroy();
3755
            }
3756
        }
3757
        */
3758
    }
3759
3760
/*******************************************************
3761
   * 11. DEBUG
3762
*******************************************************/
3763
3764
    /**
3765
     * Debug helper method.
3766
     * Can be called from /shoppingcart/debug/.
3767
     *
3768
     * @return string
3769
     */
3770
    public function debug()
3771
    {
3772
        $this->calculateOrderAttributes(true);
3773
3774
        return EcommerceTaskDebugCart::debug_object($this);
3775
    }
3776
}
3777