Order   F
last analyzed

Complexity

Total Complexity 560

Size/Duplication

Total Lines 3859
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 67

Importance

Changes 0
Metric Value
dl 0
loc 3859
rs 0.8
c 0
b 0
f 0
wmc 560
lcom 1
cbo 67

177 Methods

Rating   Name   Duplication   Size   Complexity  
A i18n_singular_name() 0 4 1
A i18n_plural_name() 0 4 1
A set_needs_recalculating() 0 4 1
A get_needs_recalculating() 0 4 2
B getModifierForms() 0 28 7
A get_order_status_options() 0 4 1
A get_by_id_if_can_view() 0 14 4
A get_datalist_of_orders_with_submit_record() 0 22 3
B scaffoldSearchFields() 0 49 10
A CMSEditLink() 0 4 1
F getCMSFields() 0 399 33
A getOrderItemsField() 0 7 1
A getModifierTableField() 0 7 1
A getBillingAddressField() 0 16 1
A getShippingAddressField() 0 16 1
A getOrderStatusLogsTableField() 0 15 2
A getOrderStatusLogsTableFieldEditable() 0 10 1
A getOrderStatusLogsTableField_Archived() 0 17 4
A getEmailsTableField() 0 8 1
A getPaymentsField() 0 9 1
A OrderStepField() 0 4 1
C init() 0 60 17
C tryToFinaliseOrder() 0 54 12
A doNextStatus() 0 15 4
B Cancel() 0 33 6
A Archive() 0 23 3
A MyStep() 0 22 5
A RelevantLogEntry() 0 4 1
A CurrentStepVisibleToCustomer() 0 12 3
A IsFirstStep() 0 12 4
A IsInCart() 0 4 2
A IsPastCart() 0 4 2
A IsUncomplete() 0 4 1
A IsProcessing() 0 4 1
A IsCompleted() 0 4 1
A IsPaid() 0 8 3
A IsPaidNice() 0 4 1
A getIsPaidNice() 0 4 2
B PaymentIsPending() 0 16 7
A RelevantPayments() 0 10 2
A IsCancelled() 0 4 1
A getIsCancelled() 0 4 2
A IsCustomerCancelled() 0 8 3
A IsAdminCancelled() 0 15 5
A ShopClosed() 0 4 1
B CreateOrReturnExistingMember() 0 23 8
C CreateOrReturnExistingAddress() 0 44 13
B SetCountryFields() 0 19 7
A SetRegionFields() 0 15 5
A UpdateCurrency() 0 13 3
A SetCurrency() 0 4 1
A sendEmail() 0 15 1
A sendAdminNotification() 0 15 1
A renderOrderInEmailFormat() 0 13 1
B prepareAndSendEmail() 0 60 10
A createReplacementArrayForEmail() 0 29 3
B getOrderAttributesByType() 0 24 8
A Items() 0 8 2
A OrderItems() 0 4 1
A Buyables() 0 10 2
A itemsFromDatabase() 0 14 3
A OrderModifiers() 0 4 1
A Modifiers() 0 4 1
A modifiersFromDatabase() 0 14 3
A calculateOrderAttributes() 0 14 6
A calculateOrderItems() 0 15 4
A calculateModifiers() 0 12 4
B ModifiersSubTotal() 0 27 10
A RetrieveModifier() 0 11 4
A getMemberForCanFunctions() 0 12 3
A canCreate() 0 11 3
B canView() 0 45 11
A canOverrideCanView() 0 21 5
A IsInSession() 0 6 3
A LessSecureSessionID() 0 8 4
A canViewAdminStuff() 0 11 3
A canEdit() 0 19 6
A canCheckout() 0 14 4
A canSubmit() 0 17 5
A canPay() 0 13 5
A canCancel() 0 17 5
A canDelete() 0 16 4
A CanViewOrderStatusLogs() 0 12 3
A CustomerViewableOrderStatusLogs() 0 14 4
A OrderEmail() 0 4 1
B getOrderEmail() 0 18 9
A HasPrintOrEmailLink() 0 4 2
A EmailLink() 0 4 1
A getEmailLink() 0 8 3
A PrintLink() 0 4 1
A getPrintLink() 0 8 3
A PackingSlipLink() 0 4 1
A getPackingSlipLink() 0 6 2
A RetrieveLink() 0 4 1
A getRetrieveLink() 0 14 3
A ShareLink() 0 4 1
A getShareLink() 0 18 2
A FeedbackLink() 0 4 1
A getFeedbackLink() 0 7 2
A DeleteLink() 0 4 1
A getDeleteLink() 0 8 2
A CopyOrderLink() 0 4 1
A getCopyOrderLink() 0 8 3
A Title() 0 4 1
F getTitle() 0 59 20
A OrderItemsSummaryNice() 0 4 1
A getOrderItemsSummaryNice() 0 4 1
A OrderItemsSummaryAsHTML() 0 26 5
A SubTotal() 0 4 1
A getSubTotal() 0 14 4
A SubTotalAsCurrencyObject() 0 4 1
A SubTotalAsMoney() 0 4 1
A getSubTotalAsMoney() 0 4 1
A ModifiersSubTotalAsCurrencyObject() 0 4 1
A ModifiersSubTotalAsMoneyObject() 0 4 1
A Total() 0 4 1
A getTotal() 0 4 1
A TotalAsCurrencyObject() 0 4 1
A TotalAsMoney() 0 4 1
A getTotalAsMoney() 0 4 1
A TotalOutstanding() 0 4 1
A getTotalOutstanding() 0 16 3
A TotalOutstandingAsCurrencyObject() 0 4 1
A TotalOutstandingAsMoney() 0 4 1
A getTotalOutstandingAsMoney() 0 4 1
A TotalPaid() 0 4 1
A getTotalPaid() 0 17 6
A TotalPaidAsCurrencyObject() 0 4 1
A TotalPaidAsMoney() 0 4 1
A getTotalPaidAsMoney() 0 4 1
A TotalItems() 0 4 1
A getTotalItems() 0 10 3
A MoreThanOneItemInCart() 0 4 2
A TotalItemsTimesQuantity() 0 4 1
A getTotalItemsTimesQuantity() 0 17 3
A Country() 0 4 1
C getCountry() 0 37 12
A IsSeparateShippingAddress() 0 4 2
A FullNameCountry() 0 4 1
A getFullNameCountry() 0 4 1
A ExpectedCountryName() 0 4 1
A getExpectedCountryName() 0 4 1
A FixedCountry() 0 4 1
A getFixedCountry() 0 9 2
A Region() 0 4 1
C getRegion() 0 33 12
A HasAlternativeCurrency() 0 4 1
A getHasAlternativeCurrency() 0 12 3
A EnsureCorrectExchangeRate() 0 18 5
A IsSubmitted() 0 4 1
A getIsSubmitted() 0 12 4
A IsArchived() 0 10 3
A SubmissionLog() 0 8 1
A OrderDate() 0 11 2
A SecondsSinceBeingSubmitted() 0 8 2
B SubmitErrors() 0 16 7
A CustomerStatus() 0 4 1
A getCustomerStatus() 0 18 6
A CanHaveShippingAddress() 0 4 1
A getCanHaveShippingAddress() 0 4 1
A DisplayPage() 0 18 5
A Link() 0 16 3
A APILink() 0 4 1
A CheckoutLink() 0 15 3
A ConvertToHTML() 0 10 1
A ConvertToString() 0 4 1
A ConvertToJSON() 0 4 1
A addHasOneAndHasManyAsVariables() 0 13 1
A AJAXDefinitions() 0 4 1
A EcomConfig() 0 4 1
B updateForAjax() 0 53 5
A SubTotalCartValue() 0 4 1
A populateDefaults() 0 4 1
A onBeforeWrite() 0 15 4
B onAfterWrite() 0 24 7
A onBeforeDelete() 0 45 3
A debug() 0 6 1

How to fix   Complexity   

Complex Class

Complex classes like Order often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Order, and based on these observations, apply Extract Interface, too.

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
32
{
33
    /**
34
     * API Control.
35
     *
36
     * @var array
37
     */
38
    private static $api_access = 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...
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...
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',
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...
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...
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...
115
        'SessionID' => true,
116
        'LastEdited' => true
117
    );
118
119
    /**
120
     * standard SS variable.
121
     *
122
     * @var string
123
     */
124
    private static $default_sort = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
125
        'LastEdited' => 'DESC',
126
        'ID' => 'DESC'
127
    ];
128
129
    /**
130
     * standard SS variable.
131
     *
132
     * @var array
133
     */
134
    private static $casting = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
135
        'OrderEmail' => 'Varchar',
136
        'EmailLink' => 'Varchar',
137
        'PrintLink' => 'Varchar',
138
        'ShareLink' => 'Varchar',
139
        'FeedbackLink' => 'Varchar',
140
        'RetrieveLink' => 'Varchar',
141
        'Title' => 'Varchar',
142
        'Total' => 'Currency',
143
        'TotalAsMoney' => 'Money',
144
        'SubTotal' => 'Currency',
145
        'SubTotalAsMoney' => 'Money',
146
        'TotalPaid' => 'Currency',
147
        'TotalPaidAsMoney' => 'Money',
148
        'TotalOutstanding' => 'Currency',
149
        'TotalOutstandingAsMoney' => 'Money',
150
        'HasAlternativeCurrency' => 'Boolean',
151
        'TotalItems' => 'Double',
152
        'TotalItemsTimesQuantity' => 'Double',
153
        'IsCancelled' => 'Boolean',
154
        'IsPaidNice' => 'Varchar',
155
        'Country' => 'Varchar(3)', //This is the applicable country for the order - for tax purposes, etc....
156
        'FullNameCountry' => 'Varchar',
157
        'IsSubmitted' => 'Boolean',
158
        'CustomerStatus' => 'Varchar',
159
        'CanHaveShippingAddress' => 'Boolean',
160
    );
161
162
    /**
163
     * standard SS variable.
164
     *
165
     * @var string
166
     */
167
    private static $singular_name = 'Order';
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
168
    public function i18n_singular_name()
169
    {
170
        return _t('Order.ORDER', 'Order');
171
    }
172
173
    /**
174
     * standard SS variable.
175
     *
176
     * @var string
177
     */
178
    private static $plural_name = 'Orders';
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
179
    public function i18n_plural_name()
180
    {
181
        return _t('Order.ORDERS', 'Orders');
182
    }
183
184
    /**
185
     * Standard SS variable.
186
     *
187
     * @var string
188
     */
189
    private static $description = "A collection of items that together make up the 'Order'.  An order can be placed.";
190
191
    /**
192
     * Tells us if an order needs to be recalculated
193
     * can save one for each order...
194
     *
195
     * @var array
196
     */
197
    private static $_needs_recalculating = array();
198
199
    /**
200
     * @param bool (optional) $b
201
     * @param int (optional)  $orderID
202
     *
203
     * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|null?

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

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

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

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

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

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

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

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
781
                $fields->addFieldToTab(
782
                    'Root.Account',
783
                    GridField::create(
784
                        'OrderFeedback',
785
                        Injector::inst()->get('OrderFeedback')->singular_name(),
786
                        OrderFeedback::get()->filter(array('OrderID' => $this->ID)),
787
                        GridFieldConfig_RecordViewer::create()
788
                    )
789
                );
790
            }
791
            $cancelledField = $fields->dataFieldByName('CancelledByID');
792
            $fields->removeByName('CancelledByID');
793
            $shopAdminAndCurrentCustomerArray = EcommerceRole::list_of_admins(true);
794
            if ($member && $member->exists()) {
795
                $shopAdminAndCurrentCustomerArray[$member->ID] = $member->getName();
796
            }
797
            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...
798
                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...
799
                    $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...
800
                }
801
            }
802
            if ($this->canCancel()) {
803
                $fields->addFieldsToTab(
804
                    'Root.Cancellations',
805
                    array(
806
                        DropdownField::create(
807
                            'CancelledByID',
808
                            $cancelledField->Title(),
809
                            $shopAdminAndCurrentCustomerArray
810
                        )
811
                    )
812
                );
813
            } else {
814
                $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...
815
                $fields->addFieldsToTab(
816
                    'Root.Cancellations',
817
                    ReadonlyField::create(
818
                        'CancelledByDisplay',
819
                        $cancelledField->Title(),
820
                        $cancelledBy
821
822
                    )
823
                );
824
            }
825
            $fields->addFieldToTab('Root.Log', $this->getOrderStatusLogsTableField_Archived());
826
            $submissionLog = $this->SubmissionLog();
827
            if ($submissionLog) {
828
                $fields->addFieldToTab(
829
                    'Root.Log',
830
                    ReadonlyField::create(
831
                        'SequentialOrderNumber',
832
                        _t('Order.SEQUENTIALORDERNUMBER', 'Consecutive order number'),
833
                        $submissionLog->SequentialOrderNumber
834
                    )->setRightTitle('e.g. 1,2,3,4,5...')
835
                );
836
            }
837
        } else {
838
            $linkText = _t(
839
                'Order.LOAD_THIS_ORDER',
840
                'load this order'
841
            );
842
            $message = _t(
843
                'Order.NOSUBMITTEDYET',
844
                '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 .',
845
                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...
846
            );
847
            $fields->addFieldToTab('Root.Next', new LiteralField('MainDetails', '<p>'.$message.'</p>'));
848
            $fields->addFieldToTab('Root.Items', $this->getOrderItemsField());
849
            $fields->addFieldToTab('Root.Extras', $this->getModifierTableField());
850
851
            //MEMBER STUFF
852
            $specialOptionsArray = array();
853
            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...
854
                $specialOptionsArray[0] = _t('Order.SELECTCUSTOMER', '--- Remover Customer ---');
855
                $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...
856
            } elseif ($currentMember) {
857
                $specialOptionsArray[0] = _t('Order.SELECTCUSTOMER', '--- Select Customers ---');
858
                $currentMemberID = $currentMember->ID;
859
                $specialOptionsArray[$currentMemberID] = _t('Order.ASSIGNTHISORDERTOME', '- Assign this order to me: ').$currentMember->getTitle();
860
            }
861
            //MEMBER FIELD!!!!!!!
862
            $memberArray = $specialOptionsArray + EcommerceRole::list_of_customers(true);
863
            $fields->addFieldToTab('Root.Next', new DropdownField('MemberID', _t('Order.SELECTCUSTOMER', 'Select Customer'), $memberArray), 'CustomerOrderNote');
864
            $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...
865
        }
866
        $fields->addFieldToTab('Root.Addresses', new HeaderField('BillingAddressHeader', _t('Order.BILLINGADDRESS', 'Billing Address')));
867
868
        $fields->addFieldToTab('Root.Addresses', $this->getBillingAddressField());
869
870
        if (EcommerceConfig::get('OrderAddress', 'use_separate_shipping_address')) {
871
            $fields->addFieldToTab('Root.Addresses', new HeaderField('ShippingAddressHeader', _t('Order.SHIPPINGADDRESS', 'Shipping Address')));
872
            $fields->addFieldToTab('Root.Addresses', new CheckboxField('UseShippingAddress', _t('Order.USESEPERATEADDRESS', 'Use separate shipping address?')));
873
            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...
874
                $fields->addFieldToTab('Root.Addresses', $this->getShippingAddressField());
875
            }
876
        }
877
        $currencies = EcommerceCurrency::get_list();
878
        if ($currencies && $currencies->count()) {
879
            $currencies = $currencies->map()->toArray();
880
            $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...
881
            $fields->addFieldToTab('Root.Currency', $currencyField = new DropdownField('CurrencyUsedID', _t('Order.CurrencyUsed', 'Currency Used'), $currencies));
882
            if ($this->IsSubmitted()) {
883
                $fields->replaceField('CurrencyUsedID', $fields->dataFieldByName('CurrencyUsedID')->performReadonlyTransformation());
884
            }
885
        } else {
886
            $fields->addFieldToTab('Root.Currency', new LiteralField('CurrencyInfo', '<p>You can not change currencies, because no currencies have been created.</p>'));
887
            $fields->replaceField('CurrencyUsedID', $fields->dataFieldByName('CurrencyUsedID')->performReadonlyTransformation());
888
        }
889
        $fields->addFieldToTab('Root.Log', new ReadonlyField('Created', _t('Root.CREATED', 'Created')));
890
        $fields->addFieldToTab('Root.Log', new ReadonlyField('LastEdited', _t('Root.LASTEDITED', 'Last saved')));
891
        $this->extend('updateCMSFields', $fields);
892
893
        return $fields;
894
    }
895
896
    /**
897
     * Field to add and edit Order Items.
898
     *
899
     * @return GridField
900
     */
901
    protected function getOrderItemsField()
902
    {
903
        $gridFieldConfig = GridFieldConfigForOrderItems::create();
904
        $source = $this->OrderItems();
905
906
        return new GridField('OrderItems', _t('OrderItems.PLURALNAME', 'Order Items'), $source, $gridFieldConfig);
907
    }
908
909
    /**
910
     * Field to add and edit Modifiers.
911
     *
912
     * @return GridField
913
     */
914
    public function getModifierTableField()
915
    {
916
        $gridFieldConfig = GridFieldConfigForOrderItems::create();
917
        $source = $this->Modifiers();
918
919
        return new GridField('OrderModifiers', _t('OrderItems.PLURALNAME', 'Order Items'), $source, $gridFieldConfig);
920
    }
921
922
    /**
923
     *@return GridField
924
     **/
925
    protected function getBillingAddressField()
926
    {
927
        $this->CreateOrReturnExistingAddress('BillingAddress');
928
        $gridFieldConfig = GridFieldConfig::create()->addComponents(
929
            new GridFieldToolbarHeader(),
930
            new GridFieldSortableHeader(),
931
            new GridFieldDataColumns(),
932
            new GridFieldPaginator(10),
933
            new GridFieldEditButton(),
934
            new GridFieldDetailForm()
935
        );
936
        //$source = $this->BillingAddress();
937
        $source = BillingAddress::get()->filter(array('OrderID' => $this->ID));
938
939
        return new GridField('BillingAddress', _t('BillingAddress.SINGULARNAME', 'Billing Address'), $source, $gridFieldConfig);
940
    }
941
942
    /**
943
     *@return GridField
944
     **/
945
    protected function getShippingAddressField()
946
    {
947
        $this->CreateOrReturnExistingAddress('ShippingAddress');
948
        $gridFieldConfig = GridFieldConfig::create()->addComponents(
949
            new GridFieldToolbarHeader(),
950
            new GridFieldSortableHeader(),
951
            new GridFieldDataColumns(),
952
            new GridFieldPaginator(10),
953
            new GridFieldEditButton(),
954
            new GridFieldDetailForm()
955
        );
956
        //$source = $this->ShippingAddress();
957
        $source = ShippingAddress::get()->filter(array('OrderID' => $this->ID));
958
959
        return new GridField('ShippingAddress', _t('BillingAddress.SINGULARNAME', 'Shipping Address'), $source, $gridFieldConfig);
960
    }
961
962
    /**
963
     * Needs to be public because the OrderStep::getCMSFIelds accesses it.
964
     *
965
     * @param string    $sourceClass
966
     * @param string    $title
967
     *
968
     * @return GridField
969
     **/
970
    public function getOrderStatusLogsTableField(
971
        $sourceClass = 'OrderStatusLog',
972
        $title = ''
973
    ) {
974
        $gridFieldConfig = GridFieldConfig_RecordViewer::create()->addComponents(
975
            new GridFieldAddNewButton('toolbar-header-right'),
976
            new GridFieldDetailForm()
977
        );
978
        $title ? $title : $title = _t('OrderStatusLog.PLURALNAME', 'Order Status Logs');
979
        $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...
980
        $gf = new GridField($sourceClass, $title, $source, $gridFieldConfig);
981
        $gf->setModelClass($sourceClass);
982
983
        return $gf;
984
    }
985
986
    /**
987
     * Needs to be public because the OrderStep::getCMSFIelds accesses it.
988
     *
989
     * @param string    $sourceClass
990
     * @param string    $title
991
     *
992
     * @return GridField
993
     **/
994
    public function getOrderStatusLogsTableFieldEditable(
995
        $sourceClass = 'OrderStatusLog',
996
        $title = ''
997
    ) {
998
        $gf = $this->getOrderStatusLogsTableField($sourceClass, $title);
999
        $gf->getConfig()->addComponents(
1000
            new GridFieldEditButton()
1001
        );
1002
        return $gf;
1003
    }
1004
1005
    /**
1006
     * @param string    $sourceClass
1007
     * @param string    $title
1008
     * @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...
1009
     * @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...
1010
     *
1011
     * @return GridField
1012
     **/
1013
    protected function getOrderStatusLogsTableField_Archived(
1014
        $sourceClass = 'OrderStatusLog',
1015
        $title = '',
1016
        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...
1017
        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...
1018
    ) {
1019
        $title ? $title : $title = _t('OrderLog.PLURALNAME', 'Order Log');
1020
        $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...
1021
        if ($sourceClass != 'OrderStatusLog' && class_exists($sourceClass)) {
1022
            $source = $source->filter(array('ClassName' => ClassInfo::subclassesFor($sourceClass)));
1023
        }
1024
        $gridField = GridField::create($sourceClass, $title, $source, $config = GridFieldConfig_RelationEditor::create());
1025
        $config->removeComponentsByType('GridFieldAddExistingAutocompleter');
1026
        $config->removeComponentsByType('GridFieldDeleteAction');
1027
1028
        return $gridField;
1029
    }
1030
1031
    /**
1032
     * @return GridField
1033
     **/
1034
    public function getEmailsTableField()
1035
    {
1036
        $gridFieldConfig = GridFieldConfig_RecordViewer::create()->addComponents(
1037
            new GridFieldDetailForm()
1038
        );
1039
1040
        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...
1041
    }
1042
1043
    /**
1044
     * @return GridField
1045
     */
1046
    protected function getPaymentsField()
1047
    {
1048
        $gridFieldConfig = GridFieldConfig_RecordViewer::create()->addComponents(
1049
            new GridFieldDetailForm(),
1050
            new GridFieldEditButton()
1051
        );
1052
1053
        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...
1054
    }
1055
1056
    /**
1057
     * @return OrderStepField
1058
     */
1059
    public function OrderStepField()
1060
    {
1061
        return OrderStepField::create($name = 'MyOrderStep', $this, Member::currentUser());
1062
    }
1063
1064
    /*******************************************************
1065
       * 2. MAIN TRANSITION FUNCTIONS
1066
    *******************************************************/
1067
1068
    /**
1069
     * init runs on start of a new Order (@see onAfterWrite)
1070
     * it adds all the modifiers to the orders and the starting OrderStep.
1071
     *
1072
     * @param bool $recalculate
1073
     *
1074
     * @return DataObject (Order)
1075
     **/
1076
    public function init($recalculate = false)
1077
    {
1078
        if ($this->IsSubmitted()) {
1079
            user_error('Can not init an order that has been submitted', E_USER_NOTICE);
1080
        } else {
1081
            //to do: check if shop is open....
1082
            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...
1083
                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...
1084
                    $createdOrderStatus = DataObject::get_one('OrderStep');
1085
                    if (!$createdOrderStatus) {
1086
                        user_error('No ordersteps have been created', E_USER_WARNING);
1087
                    }
1088
                    $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...
1089
                }
1090
                $createdModifiersClassNames = array();
1091
                $modifiersAsArrayList = new ArrayList();
1092
                $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...
1093
                if ($modifiers->count()) {
1094
                    foreach ($modifiers as $modifier) {
1095
                        $modifiersAsArrayList->push($modifier);
1096
                    }
1097
                }
1098
                if ($modifiersAsArrayList->count()) {
1099
                    foreach ($modifiersAsArrayList as $modifier) {
1100
                        $createdModifiersClassNames[$modifier->ID] = $modifier->ClassName;
1101
                    }
1102
                } else {
1103
                }
1104
                $modifiersToAdd = EcommerceConfig::get('Order', 'modifiers');
1105
                if (is_array($modifiersToAdd) && count($modifiersToAdd) > 0) {
1106
                    foreach ($modifiersToAdd as $numericKey => $className) {
1107
                        if (!in_array($className, $createdModifiersClassNames)) {
1108
                            if (class_exists($className)) {
1109
                                $modifier = new $className();
1110
                                //only add the ones that should be added automatically
1111
                                if (!$modifier->DoNotAddAutomatically()) {
1112
                                    if (is_a($modifier, 'OrderModifier')) {
1113
                                        $modifier->OrderID = $this->ID;
1114
                                        $modifier->Sort = $numericKey;
1115
                                        //init method includes a WRITE
1116
                                        $modifier->init();
1117
                                        //IMPORTANT - add as has_many relationship  (Attributes can be a modifier OR an OrderItem)
1118
                                        $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...
1119
                                        $modifiersAsArrayList->push($modifier);
1120
                                    }
1121
                                }
1122
                            } else {
1123
                                user_error('reference to a non-existing class: '.$className.' in modifiers', E_USER_NOTICE);
1124
                            }
1125
                        }
1126
                    }
1127
                }
1128
                $this->extend('onInit', $this);
1129
                //careful - this will call "onAfterWrite" again
1130
                $this->write();
1131
            }
1132
        }
1133
1134
        return $this;
1135
    }
1136
1137
    /**
1138
     * @var array
1139
     */
1140
    private static $_try_to_finalise_order_is_running = array();
1141
1142
    /**
1143
     * Goes through the order steps and tries to "apply" the next status to the order.
1144
     *
1145
     * @param bool $runAgain
1146
     * @param bool $fromOrderQueue - is it being called from the OrderProcessQueue (or similar)
1147
     *
1148
     * @return null
1149
     **/
1150
    public function tryToFinaliseOrder($runAgain = false, $fromOrderQueue = false)
1151
    {
1152
        if (empty(self::$_try_to_finalise_order_is_running[$this->ID]) || $runAgain) {
1153
            self::$_try_to_finalise_order_is_running[$this->ID] = true;
1154
1155
            //if the order has been cancelled then we do not process it ...
1156
            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...
1157
                $this->Archive(true);
1158
1159
                return;
1160
            }
1161
            // if it is in the queue it has to run from the queue tasks
1162
            // if it ruins from the queue tasks then it has to be one currently processing.
1163
            $queueObjectSingleton = Injector::inst()->get('OrderProcessQueue');
1164
            if ($myQueueObject = $queueObjectSingleton->getQueueObject($this)) {
1165
                if ($fromOrderQueue) {
1166
                    if (! $myQueueObject->InProcess) {
1167
                        return;
1168
                    }
1169
                } else {
1170
                    return;
1171
                }
1172
            }
1173
            //a little hack to make sure we do not rely on a stored value
1174
            //of "isSubmitted"
1175
            $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...
1176
            //status of order is being progressed
1177
            $nextStatusID = $this->doNextStatus();
1178
            if ($nextStatusID) {
1179
                $nextStatusObject = OrderStep::get()->byID($nextStatusID);
1180
                if ($nextStatusObject) {
1181
                    $delay = $nextStatusObject->CalculatedDeferTimeInSeconds($this);
1182
                    if ($delay > 0) {
1183
                        //adjust delay time from seconds since being submitted
1184
                        if ($nextStatusObject->DeferFromSubmitTime) {
1185
                            $delay = $delay - $this->SecondsSinceBeingSubmitted();
1186
                            if ($delay < 0) {
1187
                                $delay = 0;
1188
                            }
1189
                        }
1190
                        $queueObjectSingleton->AddOrderToQueue(
1191
                            $this,
1192
                            $delay
1193
                        );
1194
                    } else {
1195
                        //status has been completed, so it can be released
1196
                        self::$_try_to_finalise_order_is_running[$this->ID] = false;
1197
                        $this->tryToFinaliseOrder($runAgain, $fromOrderQueue);
1198
                    }
1199
                }
1200
            }
1201
            self::$_try_to_finalise_order_is_running[$this->ID] = false;
1202
        }
1203
    }
1204
1205
    /**
1206
     * Goes through the order steps and tries to "apply" the next step
1207
     * Step is updated after the other one is completed...
1208
     *
1209
     * @return int (StatusID or false if the next status can not be "applied")
1210
     **/
1211
    public function doNextStatus()
1212
    {
1213
        if ($this->MyStep()->initStep($this)) {
1214
            if ($this->MyStep()->doStep($this)) {
1215
                if ($nextOrderStepObject = $this->MyStep()->nextStep($this)) {
1216
                    $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...
1217
                    $this->write();
1218
1219
                    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...
1220
                }
1221
            }
1222
        }
1223
1224
        return 0;
1225
    }
1226
1227
    /**
1228
     * cancel an order.
1229
     *
1230
     * @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...
1231
     * @param string $reason - (optional) the reason the order is cancelled
1232
     *
1233
     * @return OrderStatusLog_Cancel
1234
     */
1235
    public function Cancel($member = null, $reason = '')
1236
    {
1237
        if ($member && $member instanceof Member) {
1238
            //we have a valid member
1239
        } else {
1240
            $member = EcommerceRole::get_default_shop_admin_user();
1241
        }
1242
        if ($member) {
1243
            //archive and write
1244
            $this->Archive($avoidWrites = true);
1245
            if ($avoidWrites) {
1246
                DB::query('Update "Order" SET CancelledByID = '.$member->ID.' WHERE ID = '.$this->ID.' LIMIT 1;');
1247
            } else {
1248
                $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...
1249
                $this->write();
1250
            }
1251
            //create log ...
1252
            $log = OrderStatusLog_Cancel::create();
1253
            $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...
1254
            $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...
1255
            $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...
1256
            if ($member->IsShopAdmin()) {
1257
                $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...
1258
            }
1259
            $log->write();
1260
            //remove from queue ...
1261
            $queueObjectSingleton = Injector::inst()->get('OrderProcessQueue');
1262
            $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...
1263
            $this->extend('doCancel', $member, $log);
1264
1265
            return $log;
1266
        }
1267
    }
1268
1269
    /**
1270
     * returns true if successful.
1271
     *
1272
     * @param bool $avoidWrites
1273
     *
1274
     * @return bool
1275
     */
1276
    public function Archive($avoidWrites = true)
1277
    {
1278
        $lastOrderStep = OrderStep::last_order_step();
1279
        if ($lastOrderStep) {
1280
            if ($avoidWrites) {
1281
                DB::query('
1282
                    UPDATE "Order"
1283
                    SET "Order"."StatusID" = '.$lastOrderStep->ID.'
1284
                    WHERE "Order"."ID" = '.$this->ID.'
1285
                    LIMIT 1
1286
                ');
1287
1288
                return true;
1289
            } else {
1290
                $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...
1291
                $this->write();
1292
1293
                return true;
1294
            }
1295
        }
1296
1297
        return false;
1298
    }
1299
1300
    /*******************************************************
1301
       * 3. STATUS RELATED FUNCTIONS / SHORTCUTS
1302
    *******************************************************/
1303
1304
    /**
1305
     * Avoids caching of $this->Status().
1306
     *
1307
     * @return DataObject (current OrderStep)
1308
     */
1309
    public function MyStep()
1310
    {
1311
        $step = null;
1312
        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...
1313
            $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...
1314
        }
1315
        if (! $step) {
1316
            $step = DataObject::get_one(
1317
                'OrderStep',
1318
                null,
1319
                $cacheDataObjectGetOne = false
1320
            );
1321
        }
1322
        if (! $step) {
1323
            $step = OrderStep_Created::create();
1324
        }
1325
        if (! $step) {
1326
            user_error('You need an order step in your Database.');
1327
        }
1328
1329
        return $step;
1330
    }
1331
1332
    /**
1333
     * Return the OrderStatusLog that is relevant to the Order status.
1334
     *
1335
     * @return OrderStatusLog
1336
     */
1337
    public function RelevantLogEntry()
1338
    {
1339
        return $this->MyStep()->RelevantLogEntry($this);
1340
    }
1341
1342
    /**
1343
     * @return OrderStep (current OrderStep that can be seen by customer)
1344
     */
1345
    public function CurrentStepVisibleToCustomer()
1346
    {
1347
        $obj = $this->MyStep();
1348
        if ($obj->HideStepFromCustomer) {
1349
            $obj = OrderStep::get()->where('"OrderStep"."Sort" < '.$obj->Sort.' AND "HideStepFromCustomer" = 0')->Last();
1350
            if (!$obj) {
1351
                $obj = DataObject::get_one('OrderStep');
1352
            }
1353
        }
1354
1355
        return $obj;
1356
    }
1357
1358
    /**
1359
     * works out if the order is still at the first OrderStep.
1360
     *
1361
     * @return bool
1362
     */
1363
    public function IsFirstStep()
1364
    {
1365
        $firstStep = DataObject::get_one('OrderStep');
1366
        $currentStep = $this->MyStep();
1367
        if ($firstStep && $currentStep) {
1368
            if ($firstStep->ID == $currentStep->ID) {
1369
                return true;
1370
            }
1371
        }
1372
1373
        return false;
1374
    }
1375
1376
    /**
1377
     * Is the order still being "edited" by the customer?
1378
     *
1379
     * @return bool
1380
     */
1381
    public function IsInCart()
1382
    {
1383
        return (bool) $this->IsSubmitted() ? false : true;
1384
    }
1385
1386
    /**
1387
     * The order has "passed" the IsInCart phase.
1388
     *
1389
     * @return bool
1390
     */
1391
    public function IsPastCart()
1392
    {
1393
        return (bool) $this->IsInCart() ? false : true;
1394
    }
1395
1396
    /**
1397
     * Are there still steps the order needs to go through?
1398
     *
1399
     * @return bool
1400
     */
1401
    public function IsUncomplete()
1402
    {
1403
        return (bool) $this->MyStep()->ShowAsUncompletedOrder;
1404
    }
1405
1406
    /**
1407
     * Is the order in the :"processing" phaase.?
1408
     *
1409
     * @return bool
1410
     */
1411
    public function IsProcessing()
1412
    {
1413
        return (bool) $this->MyStep()->ShowAsInProcessOrder;
1414
    }
1415
1416
    /**
1417
     * Is the order completed?
1418
     *
1419
     * @return bool
1420
     */
1421
    public function IsCompleted()
1422
    {
1423
        return (bool) $this->MyStep()->ShowAsCompletedOrder;
1424
    }
1425
1426
    /**
1427
     * Has the order been paid?
1428
     * TODO: why do we check if there is a total at all?
1429
     *
1430
     * @return bool
1431
     */
1432
    public function IsPaid()
1433
    {
1434
        if ($this->IsSubmitted()) {
1435
            return (bool) (($this->Total() >= 0) && ($this->TotalOutstanding() <= 0));
1436
        }
1437
1438
        return false;
1439
    }
1440
1441
    /**
1442
     * @alias for getIsPaidNice
1443
     * @return string
1444
     */
1445
    public function IsPaidNice()
1446
    {
1447
        return $this->getIsPaidNice();
1448
    }
1449
1450
1451
    public function getIsPaidNice()
1452
    {
1453
        return $this->IsPaid() ? 'yes' : 'no';
1454
    }
1455
1456
1457
    /**
1458
     * Has the order been paid?
1459
     * TODO: why do we check if there is a total at all?
1460
     *
1461
     * @return bool
1462
     */
1463
    public function PaymentIsPending()
1464
    {
1465
        if ($this->IsSubmitted()) {
1466
            if ($this->IsPaid()) {
1467
                //do nothing;
1468
            } 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...
1469
                foreach ($payments as $payment) {
1470
                    if ('Pending' == $payment->Status) {
1471
                        return true;
1472
                    }
1473
                }
1474
            }
1475
        }
1476
1477
        return false;
1478
    }
1479
1480
    /**
1481
     * shows payments that are meaningfull
1482
     * if the order has been paid then only show successful payments.
1483
     *
1484
     * @return DataList
1485
     */
1486
    public function RelevantPayments()
1487
    {
1488
        if ($this->IsPaid()) {
1489
            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...
1490
        //EcommercePayment::get()->
1491
            //	filter(array("OrderID" => $this->ID, "Status" => "Success"));
1492
        } else {
1493
            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...
1494
        }
1495
    }
1496
1497
1498
    /**
1499
     * Has the order been cancelled?
1500
     *
1501
     * @return bool
1502
     */
1503
    public function IsCancelled()
1504
    {
1505
        return $this->getIsCancelled();
1506
    }
1507
    public function getIsCancelled()
1508
    {
1509
        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...
1510
    }
1511
1512
    /**
1513
     * Has the order been cancelled by the customer?
1514
     *
1515
     * @return bool
1516
     */
1517
    public function IsCustomerCancelled()
1518
    {
1519
        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...
1520
            return true;
1521
        }
1522
1523
        return false;
1524
    }
1525
1526
    /**
1527
     * Has the order been cancelled by the  administrator?
1528
     *
1529
     * @return bool
1530
     */
1531
    public function IsAdminCancelled()
1532
    {
1533
        if ($this->IsCancelled()) {
1534
            if (!$this->IsCustomerCancelled()) {
1535
                $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...
1536
                if ($admin) {
1537
                    if ($admin->IsShopAdmin()) {
1538
                        return true;
1539
                    }
1540
                }
1541
            }
1542
        }
1543
1544
        return false;
1545
    }
1546
1547
    /**
1548
     * Is the Shop Closed for business?
1549
     *
1550
     * @return bool
1551
     */
1552
    public function ShopClosed()
1553
    {
1554
        return EcomConfig()->ShopClosed;
1555
    }
1556
1557
    /*******************************************************
1558
       * 4. LINKING ORDER WITH MEMBER AND ADDRESS
1559
    *******************************************************/
1560
1561
    /**
1562
     * Returns a member linked to the order.
1563
     * If a member is already linked, it will return the existing member.
1564
     * Otherwise it will return a new Member.
1565
     *
1566
     * Any new member is NOT written, because we dont want to create a new member unless we have to!
1567
     * We will not add a member to the order unless a new one is created in the checkout
1568
     * OR the member is logged in / logs in.
1569
     *
1570
     * Also note that if a new member is created, it is not automatically written
1571
     *
1572
     * @param bool $forceCreation - if set to true then the member will always be saved in the database.
1573
     *
1574
     * @return Member
1575
     **/
1576
    public function CreateOrReturnExistingMember($forceCreation = false)
1577
    {
1578
        if ($this->IsSubmitted()) {
1579
            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...
1580
        }
1581
        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...
1582
            $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...
1583
        } elseif ($member = Member::currentUser()) {
1584
            if (!$member->IsShopAdmin()) {
1585
                $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...
1586
                $this->write();
1587
            }
1588
        }
1589
        $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...
1590
        if (!$member) {
1591
            $member = new Member();
1592
        }
1593
        if ($member && $forceCreation) {
1594
            $member->write();
1595
        }
1596
1597
        return $member;
1598
    }
1599
1600
    /**
1601
     * Returns either the existing one or a new Order Address...
1602
     * All Orders will have a Shipping and Billing address attached to it.
1603
     * Method used to retrieve object e.g. for $order->BillingAddress(); "BillingAddress" is the method name you can use.
1604
     * If the method name is the same as the class name then dont worry about providing one.
1605
     *
1606
     * @param string $className             - ClassName of the Address (e.g. BillingAddress or ShippingAddress)
1607
     * @param string $alternativeMethodName - method to retrieve Address
1608
     **/
1609
    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...
1610
    {
1611
        if ($this->exists()) {
1612
            $methodName = $className;
1613
            if ($alternativeMethodName) {
1614
                $methodName = $alternativeMethodName;
1615
            }
1616
            if ($this->IsSubmitted()) {
1617
                return $this->$methodName();
1618
            }
1619
            $variableName = $className.'ID';
1620
            $address = null;
1621
            if ($this->$variableName) {
1622
                $address = $this->$methodName();
1623
            }
1624
            if (!$address) {
1625
                $address = new $className();
1626
                if ($member = $this->CreateOrReturnExistingMember()) {
1627
                    if ($member->exists()) {
1628
                        $address->FillWithLastAddressFromMember($member, $write = false);
1629
                    }
1630
                }
1631
            }
1632
            if ($address) {
1633
                if (!$address->exists()) {
1634
                    $address->write();
1635
                }
1636
                if ($address->OrderID != $this->ID) {
1637
                    $address->OrderID = $this->ID;
1638
                    $address->write();
1639
                }
1640
                if ($this->$variableName != $address->ID) {
1641
                    if (!$this->IsSubmitted()) {
1642
                        $this->$variableName = $address->ID;
1643
                        $this->write();
1644
                    }
1645
                }
1646
1647
                return $address;
1648
            }
1649
        }
1650
1651
        return;
1652
    }
1653
1654
    /**
1655
     * Sets the country in the billing and shipping address.
1656
     *
1657
     * @param string $countryCode            - code for the country e.g. NZ
1658
     * @param bool   $includeBillingAddress
1659
     * @param bool   $includeShippingAddress
1660
     **/
1661
    public function SetCountryFields($countryCode, $includeBillingAddress = true, $includeShippingAddress = true)
1662
    {
1663
        if ($this->IsSubmitted()) {
1664
            user_error('Can not change country in submitted order', E_USER_NOTICE);
1665
        } else {
1666
            if ($includeBillingAddress) {
1667
                if ($billingAddress = $this->CreateOrReturnExistingAddress('BillingAddress')) {
1668
                    $billingAddress->SetCountryFields($countryCode);
1669
                }
1670
            }
1671
            if (EcommerceConfig::get('OrderAddress', 'use_separate_shipping_address')) {
1672
                if ($includeShippingAddress) {
1673
                    if ($shippingAddress = $this->CreateOrReturnExistingAddress('ShippingAddress')) {
1674
                        $shippingAddress->SetCountryFields($countryCode);
1675
                    }
1676
                }
1677
            }
1678
        }
1679
    }
1680
1681
    /**
1682
     * Sets the region in the billing and shipping address.
1683
     *
1684
     * @param int $regionID - ID for the region to be set
1685
     **/
1686
    public function SetRegionFields($regionID)
1687
    {
1688
        if ($this->IsSubmitted()) {
1689
            user_error('Can not change country in submitted order', E_USER_NOTICE);
1690
        } else {
1691
            if ($billingAddress = $this->CreateOrReturnExistingAddress('BillingAddress')) {
1692
                $billingAddress->SetRegionFields($regionID);
1693
            }
1694
            if ($this->CanHaveShippingAddress()) {
1695
                if ($shippingAddress = $this->CreateOrReturnExistingAddress('ShippingAddress')) {
1696
                    $shippingAddress->SetRegionFields($regionID);
1697
                }
1698
            }
1699
        }
1700
    }
1701
1702
    /**
1703
     * Stores the preferred currency of the order.
1704
     * IMPORTANTLY we store the exchange rate for future reference...
1705
     *
1706
     * @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...
1707
     */
1708
    public function UpdateCurrency($newCurrency)
1709
    {
1710
        if ($this->IsSubmitted()) {
1711
            user_error('Can not set the currency after the order has been submitted', E_USER_NOTICE);
1712
        } else {
1713
            if (! is_a($newCurrency, Object::getCustomClass('EcommerceCurrency'))) {
1714
                $newCurrency = EcommerceCurrency::default_currency();
1715
            }
1716
            $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...
1717
            $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...
1718
            $this->write();
1719
        }
1720
    }
1721
1722
    /**
1723
     * alias for UpdateCurrency.
1724
     *
1725
     * @param EcommerceCurrency $currency
1726
     */
1727
    public function SetCurrency($currency)
1728
    {
1729
        $this->UpdateCurrency($currency);
1730
    }
1731
1732
    /*******************************************************
1733
       * 5. CUSTOMER COMMUNICATION
1734
    *******************************************************/
1735
1736
    /**
1737
     * Send the invoice of the order by email.
1738
     *
1739
     * @param string $emailClassName     (optional) class used to send email
1740
     * @param string $subject            (optional) subject for the email
1741
     * @param string $message            (optional) the main message in the email
1742
     * @param bool   $resend             (optional) send the email even if it has been sent before
1743
     * @param bool   $adminOnlyOrToEmail (optional) sends the email to the ADMIN ONLY, if you provide an email, it will go to the email...
1744
     *
1745
     * @return bool TRUE on success, FALSE on failure
1746
     */
1747
    public function sendEmail(
1748
        $emailClassName = 'Order_InvoiceEmail',
1749
        $subject = '',
1750
        $message = '',
1751
        $resend = false,
1752
        $adminOnlyOrToEmail = false
1753
    ) {
1754
        return $this->prepareAndSendEmail(
1755
            $emailClassName,
1756
            $subject,
1757
            $message,
1758
            $resend,
1759
            $adminOnlyOrToEmail
1760
        );
1761
    }
1762
1763
    /**
1764
     * Sends a message to the shop admin ONLY and not to the customer
1765
     * This can be used by ordersteps and orderlogs to notify the admin of any potential problems.
1766
     *
1767
     * @param string         $emailClassName       - (optional) template to be used ...
1768
     * @param string         $subject              - (optional) subject for the email
1769
     * @param string         $message              - (optional) message to be added with the email
1770
     * @param bool           $resend               - (optional) can it be sent twice?
1771
     * @param bool | string  $adminOnlyOrToEmail   - (optional) sends the email to the ADMIN ONLY, if you provide an email, it will go to the email...
1772
     *
1773
     * @return bool TRUE for success, FALSE for failure (not tested)
1774
     */
1775
    public function sendAdminNotification(
1776
        $emailClassName = 'Order_ErrorEmail',
1777
        $subject = '',
1778
        $message = '',
1779
        $resend = false,
1780
        $adminOnlyOrToEmail = true
1781
    ) {
1782
        return $this->prepareAndSendEmail(
1783
            $emailClassName,
1784
            $subject,
1785
            $message,
1786
            $resend,
1787
            $adminOnlyOrToEmail
1788
        );
1789
    }
1790
1791
    /**
1792
     * returns the order formatted as an email.
1793
     *
1794
     * @param string $emailClassName - template to use.
1795
     * @param string $subject        - (optional) the subject (which can be used as title in email)
1796
     * @param string $message        - (optional) the additional message
1797
     *
1798
     * @return string (html)
1799
     */
1800
    public function renderOrderInEmailFormat(
1801
        $emailClassName,
1802
        $subject = '',
1803
        $message = ''
1804
    ) {
1805
        $arrayData = $this->createReplacementArrayForEmail($subject, $message);
1806
        Config::nest();
1807
        Config::inst()->update('SSViewer', 'theme_enabled', true);
1808
        $html = $arrayData->renderWith($emailClassName);
1809
        Config::unnest();
1810
1811
        return Order_Email::emogrify_html($html);
1812
    }
1813
1814
    /**
1815
     * Send a mail of the order to the client (and another to the admin).
1816
     *
1817
     * @param string         $emailClassName       - (optional) template to be used ...
1818
     * @param string         $subject              - (optional) subject for the email
1819
     * @param string         $message              - (optional) message to be added with the email
1820
     * @param bool           $resend               - (optional) can it be sent twice?
1821
     * @param bool | string  $adminOnlyOrToEmail   - (optional) sends the email to the ADMIN ONLY, if you provide an email, it will go to the email...
1822
     *
1823
     * @return bool TRUE for success, FALSE for failure (not tested)
1824
     */
1825
    protected function prepareAndSendEmail(
1826
        $emailClassName = 'Order_InvoiceEmail',
1827
        $subject,
1828
        $message,
1829
        $resend = false,
1830
        $adminOnlyOrToEmail = false
1831
    ) {
1832
        $arrayData = $this->createReplacementArrayForEmail($subject, $message);
1833
        $from = Order_Email::get_from_email();
1834
        //why are we using this email and NOT the member.EMAIL?
1835
        //for historical reasons????
1836
        if ($adminOnlyOrToEmail) {
1837
            if (filter_var($adminOnlyOrToEmail, FILTER_VALIDATE_EMAIL)) {
1838
                $to = $adminOnlyOrToEmail;
1839
            // invalid e-mail address
1840
            } else {
1841
                $to = Order_Email::get_from_email();
1842
            }
1843
        } else {
1844
            $to = $this->getOrderEmail();
1845
        }
1846
        if ($from && $to) {
1847
            if (! class_exists($emailClassName)) {
1848
                user_error('Invalid Email ClassName provided: '. $emailClassName, E_USER_ERROR);
1849
            }
1850
            $email = new $emailClassName();
1851
            if (!(is_a($email, Object::getCustomClass('Email')))) {
1852
                user_error('No correct email class provided.', E_USER_ERROR);
1853
            }
1854
            $email->setFrom($from);
1855
            $email->setTo($to);
1856
            //we take the subject from the Array Data, just in case it has been adjusted.
1857
            $email->setSubject($arrayData->getField('Subject'));
1858
            //we also see if a CC and a BCC have been added
1859
            ;
1860
            if ($cc = $arrayData->getField('CC')) {
1861
                $email->setCc($cc);
1862
            }
1863
            if ($bcc = $arrayData->getField('BCC')) {
1864
                $email->setBcc($bcc);
1865
            }
1866
            $email->populateTemplate($arrayData);
1867
            // This might be called from within the CMS,
1868
            // so we need to restore the theme, just in case
1869
            // templates within the theme exist
1870
            Config::nest();
1871
            Config::inst()->update('SSViewer', 'theme_enabled', true);
1872
            $email->setOrder($this);
1873
            $email->setResend($resend);
1874
            $result = $email->send(null);
1875
            Config::unnest();
1876
            if (Director::isDev()) {
1877
                return true;
1878
            } else {
1879
                return $result;
1880
            }
1881
        }
1882
1883
        return false;
1884
    }
1885
1886
    /**
1887
     * returns the Data that can be used in the body of an order Email
1888
     * we add the subject here so that the subject, for example, can be added to the <title>
1889
     * of the email template.
1890
     * we add the subject here so that the subject, for example, can be added to the <title>
1891
     * of the email template.
1892
     *
1893
     * @param string $subject  - (optional) subject for email
1894
     * @param string $message  - (optional) the additional message
1895
     *
1896
     * @return ArrayData
1897
     *                   - Subject - EmailSubject
1898
     *                   - Message - specific message for this order
1899
     *                   - Message - custom message
1900
     *                   - OrderStepMessage - generic message for step
1901
     *                   - Order
1902
     *                   - EmailLogo
1903
     *                   - ShopPhysicalAddress
1904
     *                   - CurrentDateAndTime
1905
     *                   - BaseURL
1906
     *                   - CC
1907
     *                   - BCC
1908
     */
1909
    protected function createReplacementArrayForEmail($subject = '', $message = '')
1910
    {
1911
        $step = $this->MyStep();
1912
        $config = $this->EcomConfig();
1913
        $replacementArray = array();
1914
        //set subject
1915
        if (! $subject) {
1916
            $subject = $step->CalculatedEmailSubject($this);
1917
        }
1918
        if (! $message) {
1919
            $message = $step->CalculatedCustomerMessage($this);
1920
        }
1921
        $subject = str_replace('[OrderNumber]', $this->ID, $subject);
1922
        //set other variables
1923
        $replacementArray['Subject'] = $subject;
1924
        $replacementArray['To'] = '';
1925
        $replacementArray['CC'] = '';
1926
        $replacementArray['BCC'] = '';
1927
        $replacementArray['OrderStepMessage'] = $message;
1928
        $replacementArray['Order'] = $this;
1929
        $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...
1930
        $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...
1931
        $replacementArray['CurrentDateAndTime'] = DBField::create_field('SS_Datetime', 'Now');
1932
        $replacementArray['BaseURL'] = Director::baseURL();
1933
        $arrayData = ArrayData::create($replacementArray);
1934
        $this->extend('updateReplacementArrayForEmail', $arrayData);
1935
1936
        return $arrayData;
1937
    }
1938
1939
    /*******************************************************
1940
       * 6. ITEM MANAGEMENT
1941
    *******************************************************/
1942
1943
    /**
1944
     * returns a list of Order Attributes by type.
1945
     *
1946
     * @param array | String $types
1947
     *
1948
     * @return ArrayList
1949
     */
1950
    public function getOrderAttributesByType($types)
1951
    {
1952
        if (!is_array($types) && is_string($types)) {
1953
            $types = array($types);
1954
        }
1955
        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...
1956
            user_error('wrong parameter (types) provided in Order::getOrderAttributesByTypes');
1957
        }
1958
        $al = new ArrayList();
1959
        $items = $this->Items();
1960
        foreach ($items as $item) {
1961
            if (in_array($item->OrderAttributeType(), $types)) {
1962
                $al->push($item);
1963
            }
1964
        }
1965
        $modifiers = $this->Modifiers();
1966
        foreach ($modifiers as $modifier) {
1967
            if (in_array($modifier->OrderAttributeType(), $types)) {
1968
                $al->push($modifier);
1969
            }
1970
        }
1971
1972
        return $al;
1973
    }
1974
1975
    /**
1976
     * Returns the items of the order.
1977
     * Items are the order items (products) and NOT the modifiers (discount, tax, etc...).
1978
     *
1979
     * N. B. this method returns Order Items
1980
     * also see Buaybles
1981
1982
     *
1983
     * @param string filter - where statement to exclude certain items OR ClassName (e.g. 'TaxModifier')
1984
     *
1985
     * @return DataList (OrderItems)
1986
     */
1987
    public function Items($filterOrClassName = '')
1988
    {
1989
        if (!$this->exists()) {
1990
            $this->write();
1991
        }
1992
1993
        return $this->itemsFromDatabase($filterOrClassName);
1994
    }
1995
1996
    /**
1997
     * @alias function of Items
1998
     *
1999
     * N. B. this method returns Order Items
2000
     * also see Buaybles
2001
     *
2002
     * @param string filter - where statement to exclude certain items.
2003
     * @alias for Items
2004
     * @return DataList (OrderItems)
2005
     */
2006
    public function OrderItems($filterOrClassName = '')
2007
    {
2008
        return $this->Items($filterOrClassName);
2009
    }
2010
2011
    /**
2012
     * returns the buyables asscoiated with the order items.
2013
     *
2014
     * NB. this method retursn buyables
2015
     *
2016
     * @param string filter - where statement to exclude certain items.
2017
     *
2018
     * @return ArrayList (Buyables)
2019
     */
2020
    public function Buyables($filterOrClassName = '')
2021
    {
2022
        $items = $this->Items($filterOrClassName);
2023
        $arrayList = new ArrayList();
2024
        foreach ($items as $item) {
2025
            $arrayList->push($item->Buyable());
2026
        }
2027
2028
        return $arrayList;
2029
    }
2030
2031
    /**
2032
     * Return all the {@link OrderItem} instances that are
2033
     * available as records in the database.
2034
     *
2035
     * @param string filter - where statement to exclude certain items,
2036
     *   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)
2037
     *
2038
     * @return DataList (OrderItems)
2039
     */
2040
    protected function itemsFromDatabase($filterOrClassName = '')
2041
    {
2042
        $className = 'OrderItem';
2043
        $extrafilter = '';
2044
        if ($filterOrClassName) {
2045
            if (class_exists($filterOrClassName)) {
2046
                $className = $filterOrClassName;
2047
            } else {
2048
                $extrafilter = " AND $filterOrClassName";
2049
            }
2050
        }
2051
2052
        return $className::get()->filter(array('OrderID' => $this->ID))->where($extrafilter);
2053
    }
2054
2055
    /**
2056
     * @alias for Modifiers
2057
     *
2058
     * @return DataList (OrderModifiers)
2059
     */
2060
    public function OrderModifiers()
2061
    {
2062
        return $this->Modifiers();
2063
    }
2064
2065
    /**
2066
     * Returns the modifiers of the order, if it hasn't been saved yet
2067
     * it returns the modifiers from session, if it has, it returns them
2068
     * from the DB entry. ONLY USE OUTSIDE ORDER.
2069
     *
2070
     * @param string filter - where statement to exclude certain items OR ClassName (e.g. 'TaxModifier')
2071
     *
2072
     * @return DataList (OrderModifiers)
2073
     */
2074
    public function Modifiers($filterOrClassName = '')
2075
    {
2076
        return $this->modifiersFromDatabase($filterOrClassName);
2077
    }
2078
2079
    /**
2080
     * Get all {@link OrderModifier} instances that are
2081
     * available as records in the database.
2082
     * NOTE: includes REMOVED Modifiers, so that they do not get added again...
2083
     *
2084
     * @param string filter - where statement to exclude certain items OR ClassName (e.g. 'TaxModifier')
2085
     *
2086
     * @return DataList (OrderModifiers)
2087
     */
2088
    protected function modifiersFromDatabase($filterOrClassName = '')
2089
    {
2090
        $className = 'OrderModifier';
2091
        $extrafilter = '';
2092
        if ($filterOrClassName) {
2093
            if (class_exists($filterOrClassName)) {
2094
                $className = $filterOrClassName;
2095
            } else {
2096
                $extrafilter = " AND $filterOrClassName";
2097
            }
2098
        }
2099
2100
        return $className::get()->where('"OrderAttribute"."OrderID" = '.$this->ID." $extrafilter");
2101
    }
2102
2103
    /**
2104
     * Calculates and updates all the order attributes.
2105
     *
2106
     * @param bool $recalculate - run it, even if it has run already
2107
     */
2108
    public function calculateOrderAttributes($recalculate = false)
2109
    {
2110
        if ($this->IsSubmitted()) {
2111
            //submitted orders are NEVER recalculated.
2112
            //they are set in stone.
2113
        } elseif (self::get_needs_recalculating($this->ID) || $recalculate) {
2114
            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...
2115
                $this->ensureCorrectExchangeRate();
2116
                $this->calculateOrderItems($recalculate);
2117
                $this->calculateModifiers($recalculate);
2118
                $this->extend('onCalculateOrder');
2119
            }
2120
        }
2121
    }
2122
2123
    /**
2124
     * Calculates and updates all the product items.
2125
     *
2126
     * @param bool $recalculate - run it, even if it has run already
2127
     */
2128
    protected function calculateOrderItems($recalculate = false)
2129
    {
2130
        //check if order has modifiers already
2131
        //check /re-add all non-removable ones
2132
        //$start = microtime();
2133
        $orderItems = $this->itemsFromDatabase();
2134
        if ($orderItems->count()) {
2135
            foreach ($orderItems as $orderItem) {
2136
                if ($orderItem) {
2137
                    $orderItem->runUpdate($recalculate);
2138
                }
2139
            }
2140
        }
2141
        $this->extend('onCalculateOrderItems', $orderItems);
2142
    }
2143
2144
    /**
2145
     * Calculates and updates all the modifiers.
2146
     *
2147
     * @param bool $recalculate - run it, even if it has run already
2148
     */
2149
    protected function calculateModifiers($recalculate = false)
2150
    {
2151
        $createdModifiers = $this->modifiersFromDatabase();
2152
        if ($createdModifiers->count()) {
2153
            foreach ($createdModifiers as $modifier) {
2154
                if ($modifier) {
2155
                    $modifier->runUpdate($recalculate);
2156
                }
2157
            }
2158
        }
2159
        $this->extend('onCalculateModifiers', $createdModifiers);
2160
    }
2161
2162
    /**
2163
     * Returns the subtotal of the modifiers for this order.
2164
     * If a modifier appears in the excludedModifiers array, it is not counted.
2165
     *
2166
     * @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...
2167
     * @param bool         $stopAtExcludedModifier - when this flag is TRUE, we stop adding the modifiers when we reach an excluded modifier.
2168
     *
2169
     * @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...
2170
     */
2171
    public function ModifiersSubTotal($excluded = null, $stopAtExcludedModifier = false)
2172
    {
2173
        $total = 0;
2174
        $modifiers = $this->Modifiers();
2175
        if ($modifiers->count()) {
2176
            foreach ($modifiers as $modifier) {
2177
                if (!$modifier->IsRemoved()) { //we just double-check this...
2178
                    if (is_array($excluded) && in_array($modifier->ClassName, $excluded)) {
2179
                        if ($stopAtExcludedModifier) {
2180
                            break;
2181
                        }
2182
                        //do the next modifier
2183
                        continue;
2184
                    } elseif (is_string($excluded) && ($modifier->ClassName == $excluded)) {
2185
                        if ($stopAtExcludedModifier) {
2186
                            break;
2187
                        }
2188
                        //do the next modifier
2189
                        continue;
2190
                    }
2191
                    $total += $modifier->CalculationTotal();
2192
                }
2193
            }
2194
        }
2195
2196
        return $total;
2197
    }
2198
2199
    /**
2200
     * returns a modifier that is an instanceof the classname
2201
     * it extends.
2202
     *
2203
     * @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...
2204
     *
2205
     * @return DataObject (OrderModifier)
2206
     **/
2207
    public function RetrieveModifier($className)
2208
    {
2209
        $modifiers = $this->Modifiers();
2210
        if ($modifiers->count()) {
2211
            foreach ($modifiers as $modifier) {
2212
                if (is_a($modifier, Object::getCustomClass($className))) {
2213
                    return $modifier;
2214
                }
2215
            }
2216
        }
2217
    }
2218
2219
    /*******************************************************
2220
       * 7. CRUD METHODS (e.g. canView, canEdit, canDelete, etc...)
2221
    *******************************************************/
2222
2223
    /**
2224
     * @param Member $member
2225
     *
2226
     * @return DataObject (Member)
2227
     **/
2228
    //TODO: please comment why we make use of this function
2229
    protected function getMemberForCanFunctions(Member $member = null)
2230
    {
2231
        if (!$member) {
2232
            $member = Member::currentUser();
2233
        }
2234
        if (!$member) {
2235
            $member = new Member();
2236
            $member->ID = 0;
2237
        }
2238
2239
        return $member;
2240
    }
2241
2242
    /**
2243
     * @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...
2244
     *
2245
     * @return bool
2246
     **/
2247
    public function canCreate($member = null)
2248
    {
2249
        $member = $this->getMemberForCanFunctions($member);
2250
        $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...
2251
        if ($extended !== null) {
2252
            return $extended;
2253
        }
2254
        if ($member->exists()) {
2255
            return $member->IsShopAdmin();
2256
        }
2257
    }
2258
2259
    /**
2260
     * Standard SS method - can the current member view this order?
2261
     *
2262
     * @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...
2263
     *
2264
     * @return bool
2265
     **/
2266
    public function canView($member = null)
2267
    {
2268
        if (!$this->exists()) {
2269
            return true;
2270
        }
2271
        $member = $this->getMemberForCanFunctions($member);
2272
        //check if this has been "altered" in any DataExtension
2273
        $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...
2274
        if ($extended !== null) {
2275
            return $extended;
2276
        }
2277
        //is the member is a shop admin they can always view it
2278
        if (EcommerceRole::current_member_is_shop_admin($member)) {
2279
            return true;
2280
        }
2281
2282
        //is the member is a shop assistant they can always view it
2283
        if (EcommerceRole::current_member_is_shop_assistant($member)) {
2284
            return true;
2285
        }
2286
        //if the current member OWNS the order, (s)he can always view it.
2287
        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...
2288
            return true;
2289
        }
2290
        //it is the current order
2291
        if ($this->IsInSession()) {
2292
            //we do some additional CHECKS for session hackings!
2293
            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...
2294
                //can't view the order of another member!
2295
                //shop admin exemption is already captured.
2296
                //this is always true
2297
                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...
2298
                    return false;
2299
                }
2300
            } else {
2301
                //order belongs to someone, but current user is NOT logged in...
2302
                //this is allowed!
2303
                //the reason it is allowed is because we want to be able to
2304
                //add order to non-existing member
2305
                return true;
2306
            }
2307
        }
2308
2309
        return false;
2310
    }
2311
2312
    /**
2313
     * @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...
2314
     * @return bool
2315
     */
2316
    public function canOverrideCanView($member = null)
2317
    {
2318
        if ($this->canView($member)) {
2319
            //can view overrides any concerns
2320
            return true;
2321
        } else {
2322
            $tsOrder = strtotime($this->LastEdited);
2323
            $tsNow = time();
2324
            $minutes = EcommerceConfig::get('Order', 'minutes_an_order_can_be_viewed_without_logging_in');
2325
            if ($minutes && ((($tsNow - $tsOrder) / 60) < $minutes)) {
2326
2327
                //has the order been edited recently?
2328
                return true;
2329
            } elseif ($orderStep = $this->MyStep()) {
2330
2331
                // order is being processed ...
2332
                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...
2333
            }
2334
        }
2335
        return false;
2336
    }
2337
2338
    /**
2339
     * @return bool
2340
     */
2341
    public function IsInSession()
2342
    {
2343
        $orderInSession = ShoppingCart::session_order();
2344
2345
        return $orderInSession && $this->ID && $this->ID == $orderInSession->ID;
2346
    }
2347
2348
    /**
2349
     * returns a pseudo random part of the session id.
2350
     *
2351
     * @param int $size
2352
     *
2353
     * @return string
2354
     */
2355
    public function LessSecureSessionID($size = 7, $start = null)
2356
    {
2357
        if (!$start || $start < 0 || $start > (32 - $size)) {
2358
            $start = 0;
2359
        }
2360
2361
        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...
2362
    }
2363
    /**
2364
     *
2365
     * @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...
2366
     *
2367
     * @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...
2368
     **/
2369
    public function canViewAdminStuff($member = null)
2370
    {
2371
        $member = $this->getMemberForCanFunctions($member);
2372
        $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...
2373
        if ($extended !== null) {
2374
            return $extended;
2375
        }
2376
        if (Permission::checkMember($member, Config::inst()->get('EcommerceRole', 'admin_permission_code'))) {
2377
            return true;
2378
        }
2379
    }
2380
2381
    /**
2382
     * if we set canEdit to false then we
2383
     * can not see the child records
2384
     * Basically, you can edit when you can view and canEdit (even as a customer)
2385
     * Or if you are a Shop Admin you can always edit.
2386
     * Otherwise it is false...
2387
     *
2388
     * @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...
2389
     *
2390
     * @return bool
2391
     **/
2392
    public function canEdit($member = null)
2393
    {
2394
        $member = $this->getMemberForCanFunctions($member);
2395
        $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...
2396
        if ($extended !== null) {
2397
            return $extended;
2398
        }
2399
        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...
2400
            return true;
2401
        }
2402
        if (Permission::checkMember($member, Config::inst()->get('EcommerceRole', 'admin_permission_code'))) {
2403
            return true;
2404
        }
2405
        //is the member is a shop assistant they can always view it
2406
        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...
2407
            return true;
2408
        }
2409
        return false;
2410
    }
2411
2412
    /**
2413
     * is the order ready to go through to the
2414
     * checkout process.
2415
     *
2416
     * This method checks all the order items and order modifiers
2417
     * If any of them need immediate attention then this is done
2418
     * first after which it will go through to the checkout page.
2419
     *
2420
     * @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...
2421
     *
2422
     * @return bool
2423
     **/
2424
    public function canCheckout(Member $member = null)
2425
    {
2426
        $member = $this->getMemberForCanFunctions($member);
2427
        $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...
2428
        if ($extended !== null) {
2429
            return $extended;
2430
        }
2431
        $submitErrors = $this->SubmitErrors();
2432
        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...
2433
            return false;
2434
        }
2435
2436
        return true;
2437
    }
2438
2439
    /**
2440
     * Can the order be submitted?
2441
     * this method can be used to stop an order from being submitted
2442
     * due to something not being completed or done.
2443
     *
2444
     * @see Order::SubmitErrors
2445
     *
2446
     * @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...
2447
     *
2448
     * @return bool
2449
     **/
2450
    public function canSubmit(Member $member = null)
2451
    {
2452
        $member = $this->getMemberForCanFunctions($member);
2453
        $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...
2454
        if ($extended !== null) {
2455
            return $extended;
2456
        }
2457
        if ($this->IsSubmitted()) {
2458
            return false;
2459
        }
2460
        $submitErrors = $this->SubmitErrors();
2461
        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...
2462
            return false;
2463
        }
2464
2465
        return true;
2466
    }
2467
2468
    /**
2469
     * Can a payment be made for this Order?
2470
     *
2471
     * @param Member $member
0 ignored issues
show
Documentation introduced by
Should the type for parameter $member not be null|Member?

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

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

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

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

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

Loading history...
2729
     */
2730
    public function getFeedbackLink()
2731
    {
2732
        $orderConfirmationPage = DataObject::get_one('OrderConfirmationPage');
2733
        if ($orderConfirmationPage->IsFeedbackEnabled) {
2734
            return Director::AbsoluteURL($this->getRetrieveLink()).'#OrderForm_Feedback_FeedbackForm';
0 ignored issues
show
Security Bug introduced by
It seems like $this->getRetrieveLink() targeting Order::getRetrieveLink() can also be of type false; however, Director::absoluteURL() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
2735
        }
2736
    }
2737
2738
    /**
2739
     * link to delete order.
2740
     *
2741
     * @return string
2742
     */
2743
    public function DeleteLink()
2744
    {
2745
        return $this->getDeleteLink();
2746
    }
2747
    public function getDeleteLink()
2748
    {
2749
        if ($this->canDelete()) {
2750
            return ShoppingCart_Controller::delete_order_link($this->ID);
2751
        } else {
2752
            return '';
2753
        }
2754
    }
2755
2756
    /**
2757
     * link to copy order.
2758
     *
2759
     * @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...
2760
     */
2761
    public function CopyOrderLink()
2762
    {
2763
        return $this->getCopyOrderLink();
2764
    }
2765
    public function getCopyOrderLink()
2766
    {
2767
        if ($this->canView() && $this->IsSubmitted()) {
2768
            return ShoppingCart_Controller::copy_order_link($this->ID);
2769
        } else {
2770
            return '';
2771
        }
2772
    }
2773
2774
    /**
2775
     * A "Title" for the order, which summarises the main details (date, and customer) in a string.
2776
     *
2777
     * @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...
2778
     * @param bool   $includeName - e.g. by Mr Johnson
2779
     *
2780
     * @return string
2781
     **/
2782
    public function Title($dateFormat = null, $includeName = false)
2783
    {
2784
        return $this->getTitle($dateFormat, $includeName);
2785
    }
2786
    public function getTitle($dateFormat = null, $includeName = false)
2787
    {
2788
        if ($this->exists()) {
2789
            if ($dateFormat === null) {
2790
                $dateFormat = EcommerceConfig::get('Order', 'date_format_for_title');
2791
            }
2792
            if ($includeName === null) {
2793
                $includeName = EcommerceConfig::get('Order', 'include_customer_name_in_title');
2794
            }
2795
            $title = $this->i18n_singular_name()." #".number_format($this->ID);
2796
            if ($dateFormat) {
2797
                if ($submissionLog = $this->SubmissionLog()) {
2798
                    $dateObject = $submissionLog->dbObject('Created');
2799
                    $placed = _t('Order.PLACED', 'placed');
2800
                } else {
2801
                    $dateObject = $this->dbObject('Created');
2802
                    $placed = _t('Order.STARTED', 'started');
2803
                }
2804
                $title .= ' - '.$placed.' '.$dateObject->Format($dateFormat);
2805
            }
2806
            $name = '';
2807
            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...
2808
                $name = ' - '._t('Order.CANCELLED', 'CANCELLED');
2809
            }
2810
            if ($includeName) {
2811
                $by = _t('Order.BY', 'by');
2812
                if (!$name) {
2813
                    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...
2814
                        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...
2815
                            $name = ' - '.$by.' '.$billingAddress->Prefix.' '.$billingAddress->FirstName.' '.$billingAddress->Surname;
2816
                        }
2817
                    }
2818
                }
2819
                if (!$name) {
2820
                    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...
2821
                        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...
2822
                            if ($member->exists()) {
2823
                                if ($memberName = $member->getName()) {
2824
                                    if (!trim($memberName)) {
2825
                                        $memberName = _t('Order.ANONYMOUS', 'anonymous');
2826
                                    }
2827
                                    $name = ' - '.$by.' '.$memberName;
2828
                                }
2829
                            }
2830
                        }
2831
                    }
2832
                }
2833
            }
2834
            $title .= $name;
2835
        } else {
2836
            $title = _t('Order.NEW', 'New').' '.$this->i18n_singular_name();
2837
        }
2838
        $extendedTitle = $this->extend('updateTitle', $title);
2839
        if ($extendedTitle !== null && is_array($extendedTitle) && count($extendedTitle)) {
2840
            $title = implode('; ', $extendedTitle);
2841
        }
2842
2843
        return $title;
2844
    }
2845
2846
    public function OrderItemsSummaryNice()
2847
    {
2848
        return $this->getOrderItemsSummaryNice();
2849
    }
2850
2851
    public function getOrderItemsSummaryNice()
2852
    {
2853
        return DBField::create_field('HTMLText', $this->OrderItemsSummaryAsHTML());
2854
    }
2855
2856
    public function OrderItemsSummaryAsHTML()
2857
    {
2858
        $html = '';
2859
        $x = 0;
2860
        $count = $this->owner->OrderItems()->count();
0 ignored issues
show
Documentation introduced by
The property owner 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...
2861
        if ($count > 0) {
2862
            $html .= '<ul class="order-items-summary">';
2863
            foreach ($this->owner->OrderItems() as $orderItem) {
0 ignored issues
show
Documentation introduced by
The property owner 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...
2864
                $x++;
2865
                $buyable = $orderItem->Buyable();
2866
                $html .= '<li style="font-family: monospace; font-size: 0.9em; color: #1F9433;">- '.$orderItem->Quantity.'x ';
2867
                if ($buyable) {
2868
                    $html .= $buyable->InternalItemID .' '.$buyable->Title;
2869
                } else {
2870
                    $html .= $orderItem->BuyableFullName;
2871
                }
2872
                $html .= '</li>';
2873
                if ($x > 3) {
2874
                    $html .= '<li style="font-family: monospace; font-size: 0.9em; color: black;">- open for more items</li>';
2875
                    break;
2876
                }
2877
            }
2878
            $html .= '</ul>';
2879
        }
2880
        return $html;
2881
    }
2882
2883
    /**
2884
     * Returns the subtotal of the items for this order.
2885
     *
2886
     * @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...
2887
     */
2888
    public function SubTotal()
2889
    {
2890
        return $this->getSubTotal();
2891
    }
2892
    public function getSubTotal()
2893
    {
2894
        $result = 0;
2895
        $items = $this->Items();
2896
        if ($items->count()) {
2897
            foreach ($items as $item) {
2898
                if (is_a($item, Object::getCustomClass('OrderAttribute'))) {
2899
                    $result += $item->Total();
2900
                }
2901
            }
2902
        }
2903
2904
        return $result;
2905
    }
2906
2907
    /**
2908
     * @return Currency (DB Object)
2909
     **/
2910
    public function SubTotalAsCurrencyObject()
2911
    {
2912
        return DBField::create_field('Currency', $this->SubTotal());
2913
    }
2914
2915
    /**
2916
     * @return Money
2917
     **/
2918
    public function SubTotalAsMoney()
2919
    {
2920
        return $this->getSubTotalAsMoney();
2921
    }
2922
    public function getSubTotalAsMoney()
2923
    {
2924
        return EcommerceCurrency::get_money_object_from_order_currency($this->SubTotal(), $this);
2925
    }
2926
2927
    /**
2928
     * @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...
2929
     * @param bool         $stopAtExcludedModifier - when this flag is TRUE, we stop adding the modifiers when we reach an excluded modifier.
2930
     *
2931
     * @return Currency (DB Object)
2932
     **/
2933
    public function ModifiersSubTotalAsCurrencyObject($excluded = null, $stopAtExcludedModifier = false)
2934
    {
2935
        return DBField::create_field('Currency', $this->ModifiersSubTotal($excluded, $stopAtExcludedModifier));
2936
    }
2937
2938
    /**
2939
     * @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...
2940
     * @param bool         $stopAtExcludedModifier - when this flag is TRUE, we stop adding the modifiers when we reach an excluded modifier.
2941
     *
2942
     * @return Money (DB Object)
2943
     **/
2944
    public function ModifiersSubTotalAsMoneyObject($excluded = null, $stopAtExcludedModifier = false)
2945
    {
2946
        return EcommerceCurrency::get_money_object_from_order_currency($this->ModifiersSubTotal($excluded, $stopAtExcludedModifier), $this);
2947
    }
2948
2949
    /**
2950
     * Returns the total cost of an order including the additional charges or deductions of its modifiers.
2951
     *
2952
     * @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...
2953
     */
2954
    public function Total()
2955
    {
2956
        return $this->getTotal();
2957
    }
2958
    public function getTotal()
2959
    {
2960
        return $this->SubTotal() + $this->ModifiersSubTotal();
2961
    }
2962
2963
    /**
2964
     * @return Currency (DB Object)
2965
     **/
2966
    public function TotalAsCurrencyObject()
2967
    {
2968
        return DBField::create_field('Currency', $this->Total());
2969
    }
2970
2971
    /**
2972
     * @return Money
2973
     **/
2974
    public function TotalAsMoney()
2975
    {
2976
        return $this->getTotalAsMoney();
2977
    }
2978
    public function getTotalAsMoney()
2979
    {
2980
        return EcommerceCurrency::get_money_object_from_order_currency($this->Total(), $this);
2981
    }
2982
2983
    /**
2984
     * Checks to see if any payments have been made on this order
2985
     * and if so, subracts the payment amount from the order.
2986
     *
2987
     * @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...
2988
     **/
2989
    public function TotalOutstanding()
2990
    {
2991
        return $this->getTotalOutstanding();
2992
    }
2993
    public function getTotalOutstanding()
2994
    {
2995
        if ($this->IsSubmitted()) {
2996
            $total = $this->Total();
2997
            $paid = $this->TotalPaid();
2998
            $outstanding = $total - $paid;
2999
            $maxDifference = EcommerceConfig::get('Order', 'maximum_ignorable_sales_payments_difference');
3000
            if (abs($outstanding) < $maxDifference) {
3001
                $outstanding = 0;
3002
            }
3003
3004
            return floatval($outstanding);
3005
        } else {
3006
            return 0;
3007
        }
3008
    }
3009
3010
    /**
3011
     * @return Currency (DB Object)
3012
     **/
3013
    public function TotalOutstandingAsCurrencyObject()
3014
    {
3015
        return DBField::create_field('Currency', $this->TotalOutstanding());
3016
    }
3017
3018
    /**
3019
     * @return Money
3020
     **/
3021
    public function TotalOutstandingAsMoney()
3022
    {
3023
        return $this->getTotalOutstandingAsMoney();
3024
    }
3025
    public function getTotalOutstandingAsMoney()
3026
    {
3027
        return EcommerceCurrency::get_money_object_from_order_currency($this->TotalOutstanding(), $this);
3028
    }
3029
3030
    /**
3031
     * @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...
3032
     */
3033
    public function TotalPaid()
3034
    {
3035
        return $this->getTotalPaid();
3036
    }
3037
    public function getTotalPaid()
3038
    {
3039
        $paid = 0;
3040
        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...
3041
            foreach ($payments as $payment) {
3042
                if ($payment->Status == 'Success') {
3043
                    $paid += $payment->Amount->getAmount();
3044
                }
3045
            }
3046
        }
3047
        $reverseExchange = 1;
3048
        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...
3049
            $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...
3050
        }
3051
3052
        return $paid * $reverseExchange;
3053
    }
3054
3055
    /**
3056
     * @return Currency (DB Object)
3057
     **/
3058
    public function TotalPaidAsCurrencyObject()
3059
    {
3060
        return DBField::create_field('Currency', $this->TotalPaid());
3061
    }
3062
3063
    /**
3064
     * @return Money
3065
     **/
3066
    public function TotalPaidAsMoney()
3067
    {
3068
        return $this->getTotalPaidAsMoney();
3069
    }
3070
    public function getTotalPaidAsMoney()
3071
    {
3072
        return EcommerceCurrency::get_money_object_from_order_currency($this->TotalPaid(), $this);
3073
    }
3074
3075
    /**
3076
     * returns the total number of OrderItems (not modifiers).
3077
     * This is meant to run as fast as possible to quickly check
3078
     * if there is anything in the cart.
3079
     *
3080
     * @param bool $recalculate - do we need to recalculate (value is retained during lifetime of Object)
3081
     *
3082
     * @return int
3083
     **/
3084
    public function TotalItems($recalculate = false)
3085
    {
3086
        return $this->getTotalItems($recalculate);
3087
    }
3088
    public function getTotalItems($recalculate = false)
3089
    {
3090
        if ($this->totalItems === null || $recalculate) {
3091
            $this->totalItems = OrderItem::get()
3092
                ->where('"OrderAttribute"."OrderID" = '.$this->ID.' AND "OrderItem"."Quantity" > 0')
3093
                ->count();
3094
        }
3095
3096
        return $this->totalItems;
3097
    }
3098
3099
    /**
3100
     * Little shorthand.
3101
     *
3102
     * @param bool $recalculate
3103
     *
3104
     * @return bool
3105
     **/
3106
    public function MoreThanOneItemInCart($recalculate = false)
3107
    {
3108
        return $this->TotalItems($recalculate) > 1 ? true : false;
3109
    }
3110
3111
    /**
3112
     * returns the total number of OrderItems (not modifiers) times their respectective quantities.
3113
     *
3114
     * @param bool $recalculate - force recalculation
3115
     *
3116
     * @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...
3117
     **/
3118
    public function TotalItemsTimesQuantity($recalculate = false)
3119
    {
3120
        return $this->getTotalItemsTimesQuantity($recalculate);
3121
    }
3122
    public function getTotalItemsTimesQuantity($recalculate = false)
3123
    {
3124
        if ($this->totalItemsTimesQuantity === null || $recalculate) {
3125
            //to do, why do we check if you can edit ????
3126
            $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...
3127
                '
3128
                SELECT SUM("OrderItem"."Quantity")
3129
                FROM "OrderItem"
3130
                    INNER JOIN "OrderAttribute" ON "OrderAttribute"."ID" = "OrderItem"."ID"
3131
                WHERE
3132
                    "OrderAttribute"."OrderID" = '.$this->ID.'
3133
                    AND "OrderItem"."Quantity" > 0'
3134
            )->value();
3135
        }
3136
3137
        return $this->totalItemsTimesQuantity - 0;
3138
    }
3139
3140
    /**
3141
     *
3142
     * @return string (country code)
3143
     **/
3144
    public function Country()
3145
    {
3146
        return $this->getCountry();
3147
    }
3148
3149
    /**
3150
    * Returns the country code for the country that applies to the order.
3151
    * @alias  for getCountry
3152
    *
3153
    * @return string - country code e.g. NZ
3154
     */
3155
    public function getCountry()
3156
    {
3157
        $countryCodes = array(
3158
            'Billing' => '',
3159
            'Shipping' => '',
3160
        );
3161
        $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...
3162
        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...
3163
            $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...
3164
            if ($billingAddress) {
3165
                if ($billingAddress->Country) {
3166
                    $countryCodes['Billing'] = $billingAddress->Country;
3167
                }
3168
            }
3169
        }
3170
        if ($this->IsSeparateShippingAddress()) {
3171
            $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...
3172
            if ($shippingAddress) {
3173
                if ($shippingAddress->ShippingCountry) {
3174
                    $countryCodes['Shipping'] = $shippingAddress->ShippingCountry;
3175
                }
3176
            }
3177
        }
3178
        if (
3179
            (EcommerceConfig::get('OrderAddress', 'use_shipping_address_for_main_region_and_country') && $countryCodes['Shipping'])
3180
            ||
3181
            (!$countryCodes['Billing'] && $countryCodes['Shipping'])
3182
        ) {
3183
            $code = $countryCodes['Shipping'];
3184
        } elseif ($countryCodes['Billing']) {
3185
            $code = $countryCodes['Billing'];
3186
        } else {
3187
            $code = EcommerceCountry::get_country_from_ip();
3188
        }
3189
3190
        return $code;
3191
    }
3192
3193
    /**
3194
     * is this a gift / separate shippingAddress?
3195
     * @return bool
3196
     */
3197
    public function IsSeparateShippingAddress()
3198
    {
3199
        return $this->ShippingAddressID && $this->UseShippingAddress;
0 ignored issues
show
Documentation introduced by
The property ShippingAddressID does not exist on object<Order>. Since you implemented __get, maybe consider adding a @property annotation.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

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

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

Loading history...
3200
    }
3201
3202
    /**
3203
     * @alias for getFullNameCountry
3204
     *
3205
     * @return string - country name
3206
     **/
3207
    public function FullNameCountry()
3208
    {
3209
        return $this->getFullNameCountry();
3210
    }
3211
3212
    /**
3213
     * returns name of coutry.
3214
     *
3215
     * @return string - country name
3216
     **/
3217
    public function getFullNameCountry()
3218
    {
3219
        return EcommerceCountry::find_title($this->Country());
3220
    }
3221
3222
    /**
3223
     * @alis for getExpectedCountryName
3224
     * @return string - country name
3225
     **/
3226
    public function ExpectedCountryName()
3227
    {
3228
        return $this->getExpectedCountryName();
3229
    }
3230
3231
    /**
3232
     * returns name of coutry that we expect the customer to have
3233
     * This takes into consideration more than just what has been entered
3234
     * for example, it looks at GEO IP.
3235
     *
3236
     * @todo: why do we dont return a string IF there is only one item.
3237
     *
3238
     * @return string - country name
3239
     **/
3240
    public function getExpectedCountryName()
3241
    {
3242
        return EcommerceCountry::find_title(EcommerceCountry::get_country(false, $this->ID));
3243
    }
3244
3245
    /**
3246
     * return the title of the fixed country (if any).
3247
     *
3248
     * @return string | empty string
3249
     **/
3250
    public function FixedCountry()
3251
    {
3252
        return $this->getFixedCountry();
3253
    }
3254
    public function getFixedCountry()
3255
    {
3256
        $code = EcommerceCountry::get_fixed_country_code();
3257
        if ($code) {
3258
            return EcommerceCountry::find_title($code);
3259
        }
3260
3261
        return '';
3262
    }
3263
3264
    /**
3265
     * Returns the region that applies to the order.
3266
     * we check both billing and shipping, in case one of them is empty.
3267
     *
3268
     * @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...
3269
     **/
3270
    public function Region()
3271
    {
3272
        return $this->getRegion();
3273
    }
3274
    public function getRegion()
3275
    {
3276
        $regionIDs = array(
3277
            'Billing' => 0,
3278
            'Shipping' => 0,
3279
        );
3280
        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...
3281
            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...
3282
                if ($billingAddress->RegionID) {
3283
                    $regionIDs['Billing'] = $billingAddress->RegionID;
3284
                }
3285
            }
3286
        }
3287
        if ($this->CanHaveShippingAddress()) {
3288
            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...
3289
                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...
3290
                    if ($shippingAddress->ShippingRegionID) {
3291
                        $regionIDs['Shipping'] = $shippingAddress->ShippingRegionID;
3292
                    }
3293
                }
3294
            }
3295
        }
3296
        if (count($regionIDs)) {
3297
            //note the double-check with $this->CanHaveShippingAddress() and get_use_....
3298
            if ($this->CanHaveShippingAddress() && EcommerceConfig::get('OrderAddress', 'use_shipping_address_for_main_region_and_country') && $regionIDs['Shipping']) {
3299
                return EcommerceRegion::get()->byID($regionIDs['Shipping']);
3300
            } else {
3301
                return EcommerceRegion::get()->byID($regionIDs['Billing']);
3302
            }
3303
        } else {
3304
            return EcommerceRegion::get()->byID(EcommerceRegion::get_region_from_ip());
3305
        }
3306
    }
3307
3308
    /**
3309
     * Casted variable
3310
     * Currency is not the same as the standard one?
3311
     *
3312
     * @return bool
3313
     **/
3314
    public function HasAlternativeCurrency()
3315
    {
3316
        return $this->getHasAlternativeCurrency();
3317
    }
3318
    public function getHasAlternativeCurrency()
3319
    {
3320
        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...
3321
            if ($currency->IsDefault()) {
0 ignored issues
show
Coding Style introduced by
The if-else statement can be simplified to return !$currency->IsDefault();.
Loading history...
3322
                return false;
3323
            } else {
3324
                return true;
3325
            }
3326
        } else {
3327
            return false;
3328
        }
3329
    }
3330
3331
    /**
3332
     * Makes sure exchange rate is updated and maintained before order is submitted
3333
     * This method is public because it could be called from a shopping Cart Object.
3334
     **/
3335
    public function EnsureCorrectExchangeRate()
3336
    {
3337
        if (!$this->IsSubmitted()) {
3338
            $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...
3339
            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...
3340
                if ($currency->IsDefault()) {
3341
                    $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...
3342
                } else {
3343
                    $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...
3344
                }
3345
            } else {
3346
                $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...
3347
            }
3348
            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...
3349
                $this->write();
3350
            }
3351
        }
3352
    }
3353
3354
    /**
3355
     * speeds up processing by storing the IsSubmitted value
3356
     * we start with -1 to know if it has been requested before.
3357
     *
3358
     * @var bool
3359
     */
3360
    protected $_isSubmittedTempVar = -1;
3361
3362
    /**
3363
     * Casted variable - has the order been submitted?
3364
     * alias
3365
     * @param bool $recalculate
3366
     *
3367
     * @return bool
3368
     **/
3369
    public function IsSubmitted($recalculate = true)
3370
    {
3371
        return $this->getIsSubmitted($recalculate);
3372
    }
3373
3374
    /**
3375
     * Casted variable - has the order been submitted?
3376
     *
3377
     * @param bool $recalculate
3378
     *
3379
     * @return bool
3380
     **/
3381
    public function getIsSubmitted($recalculate = false)
3382
    {
3383
        if ($this->_isSubmittedTempVar === -1 || $recalculate) {
3384
            if ($this->SubmissionLog()) {
3385
                $this->_isSubmittedTempVar = true;
3386
            } else {
3387
                $this->_isSubmittedTempVar = false;
3388
            }
3389
        }
3390
3391
        return $this->_isSubmittedTempVar;
3392
    }
3393
3394
    /**
3395
     *
3396
     *
3397
     * @return bool
3398
     */
3399
    public function IsArchived()
3400
    {
3401
        $lastStep = OrderStep::last_order_step();
3402
        if ($lastStep) {
3403
            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...
3404
                return true;
3405
            }
3406
        }
3407
        return false;
3408
    }
3409
3410
    /**
3411
     * Submission Log for this Order (if any).
3412
     *
3413
     * @return Submission Log (OrderStatusLog_Submitted) | Null
3414
     **/
3415
    public function SubmissionLog()
3416
    {
3417
        $className = EcommerceConfig::get('OrderStatusLog', 'order_status_log_class_used_for_submitting_order');
3418
3419
        return $className::get()
3420
            ->Filter(array('OrderID' => $this->ID))
3421
            ->Last();
3422
    }
3423
3424
    /**
3425
     * Submission Log for this Order (if any).
3426
     *
3427
     * @return DateTime
0 ignored issues
show
Documentation introduced by
Should the return type not be DBField?

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

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