Passed
Pull Request — master (#313)
by Matthew
05:20
created

Order   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 357
Duplicated Lines 0 %

Test Coverage

Coverage 40%

Importance

Changes 0
Metric Value
wmc 28
dl 0
loc 357
ccs 42
cts 105
cp 0.4
rs 10
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A canEdit() 0 3 1
A fieldLabels() 0 18 1
A parseOrder() 0 12 2
A parseOrderInfo() 0 14 2
A ReceiptLink() 0 3 1
A getReceiptLink() 0 8 1
A providePermissions() 0 4 1
A onBeforeWrite() 0 4 1
A canCreate() 0 3 1
A getDecryptedResponse() 0 7 2
B parseOrderDetails() 0 54 8
A canView() 0 3 1
A canDelete() 0 3 1
B parseOrderCustomer() 0 31 5
1
<?php
2
3
namespace Dynamic\FoxyStripe\Model;
4
5
use Dynamic\FoxyStripe\Page\ProductPage;
6
use SilverStripe\Core\Config\Config;
7
use SilverStripe\Forms\DateField;
8
use SilverStripe\ORM\DataObject;
9
use SilverStripe\ORM\FieldType\DBHTMLVarchar;
10
use SilverStripe\Security\Member;
11
use SilverStripe\Security\Permission;
12
use SilverStripe\Security\PermissionProvider;
13
use SilverStripe\Security\Security;
14
15
/**
16
 * Class Order
17
 * @package Dynamic\FoxyStripe\Model
18
 *
19
 * @property \SilverStripe\ORM\FieldType\DBInt Order_ID
20
 * @property \SilverStripe\ORM\FieldType\DBDatetime TransactionDate
21
 * @property \SilverStripe\ORM\FieldType\DBCurrency ProductTotal
22
 * @property \SilverStripe\ORM\FieldType\DBCurrency TaxTotal
23
 * @property \SilverStripe\ORM\FieldType\DBCurrency ShippingTotal
24
 * @property \SilverStripe\ORM\FieldType\DBCurrency OrderTotal
25
 * @property \SilverStripe\ORM\FieldType\DBVarchar ReceiptURL
26
 * @property \SilverStripe\ORM\FieldType\DBVarchar OrderStatus
27
 * @property \SilverStripe\ORM\FieldType\DBVarchar Response
28
 *
29
 * @property int MemberID
30
 * @method Member Member
31
 *
32
 * @method \SilverStripe\ORM\HasManyList Details
33
 */
34
class Order extends DataObject implements PermissionProvider
35
{
36
    /**
37
     * @var array
38
     */
39
    private static $db = array(
0 ignored issues
show
introduced by
The private property $db is not used, and could be removed.
Loading history...
40
        'Order_ID' => 'Int',
41
        'TransactionDate' => 'DBDatetime',
42
        'ProductTotal' => 'Currency',
43
        'TaxTotal' => 'Currency',
44
        'ShippingTotal' => 'Currency',
45
        'OrderTotal' => 'Currency',
46
        'ReceiptURL' => 'Varchar(255)',
47
        'OrderStatus' => 'Varchar(255)',
48
        'Response' => 'Text',
49
    );
50
51
    /**
52
     * @var array
53
     */
54
    private static $has_one = array(
0 ignored issues
show
introduced by
The private property $has_one is not used, and could be removed.
Loading history...
55
        'Member' => Member::class,
56
    );
57
58
    /**
59
     * @var array
60
     */
61
    private static $has_many = array(
0 ignored issues
show
introduced by
The private property $has_many is not used, and could be removed.
Loading history...
62
        'Details' => OrderDetail::class,
63
    );
64
65
    /**
66
     * @var string
67
     */
68
    private static $singular_name = 'Order';
0 ignored issues
show
introduced by
The private property $singular_name is not used, and could be removed.
Loading history...
69
70
    /**
71
     * @var string
72
     */
73
    private static $plural_name = 'Orders';
0 ignored issues
show
introduced by
The private property $plural_name is not used, and could be removed.
Loading history...
74
75
    /**
76
     * @var string
77
     */
78
    private static $description = 'Orders from FoxyCart Datafeed';
0 ignored issues
show
introduced by
The private property $description is not used, and could be removed.
Loading history...
79
80
    /**
81
     * @var string
82
     */
83
    private static $default_sort = 'TransactionDate DESC, ID DESC';
0 ignored issues
show
introduced by
The private property $default_sort is not used, and could be removed.
Loading history...
84
85
    /**
86
     * @var array
87
     */
88
    private static $summary_fields = array(
0 ignored issues
show
introduced by
The private property $summary_fields is not used, and could be removed.
Loading history...
89
        'Order_ID',
90
        'TransactionDate.NiceUS',
91
        'Member.Name',
92
        'ProductTotal.Nice',
93
        'ShippingTotal.Nice',
94
        'TaxTotal.Nice',
95
        'OrderTotal.Nice',
96
        'ReceiptLink',
97
    );
98
99
    /**
100
     * @var array
101
     */
102
    private static $searchable_fields = array(
0 ignored issues
show
introduced by
The private property $searchable_fields is not used, and could be removed.
Loading history...
103
        'Order_ID',
104
        'TransactionDate' => array(
105
            'field' => DateField::class,
106
            'filter' => 'PartialMatchFilter',
107
        ),
108
        'Member.ID',
109
        'OrderTotal',
110
        'Details.ProductID',
111
    );
112
113
    /**
114
     * @var array
115
     */
116
    private static $casting = array(
0 ignored issues
show
introduced by
The private property $casting is not used, and could be removed.
Loading history...
117
        'ReceiptLink' => 'HTMLVarchar',
118
    );
119
120
    /**
121
     * @var array
122
     */
123
    private static $indexes = array(
0 ignored issues
show
introduced by
The private property $indexes is not used, and could be removed.
Loading history...
124
        'Order_ID' => true, // make unique
125
    );
126
127
    /**
128
     * @var string
129
     */
130
    private static $table_name = 'FS_Order';
0 ignored issues
show
introduced by
The private property $table_name is not used, and could be removed.
Loading history...
131
132
    /**
133
     * @param bool $includerelations
134
     *
135
     * @return array|string
136
     */
137 1
    public function fieldLabels($includerelations = true)
138
    {
139 1
        $labels = parent::fieldLabels();
140
141 1
        $labels['Order_ID'] = _t('Order.Order_ID', 'Order ID#');
142 1
        $labels['TransactionDate'] = _t('Order.TransactionDate', 'Date');
143 1
        $labels['TransactionDate.NiceUS'] = _t('Order.TransactionDate', 'Date');
144 1
        $labels['Member.Name'] = _t('Order.MemberName', 'Customer');
145 1
        $labels['Member.ID'] = _t('Order.MemberName', 'Customer');
146 1
        $labels['ProductTotal.Nice'] = _t('Order.ProductTotal', 'Sub Total');
147 1
        $labels['TaxTotal.Nice'] = _t('Order.TaxTotal', 'Tax');
148 1
        $labels['ShippingTotal.Nice'] = _t('Order.ShippingTotal', 'Shipping');
149 1
        $labels['OrderTotal'] = _t('Order.OrderTotal', 'Total');
150 1
        $labels['OrderTotal.Nice'] = _t('Order.OrderTotal', 'Total');
151 1
        $labels['ReceiptLink'] = _t('Order.ReceiptLink', 'Invoice');
152 1
        $labels['Details.ProductID'] = _t('Order.Details.ProductID', 'Product');
153
154 1
        return $labels;
155
    }
156
157
    /**
158
     * @return mixed
159
     */
160 1
    public function ReceiptLink()
161
    {
162 1
        return $this->getReceiptLink();
163
    }
164
165
    /**
166
     * @return mixed
167
     */
168 1
    public function getReceiptLink()
169
    {
170 1
        $obj = DBHTMLVarchar::create();
171 1
        $obj->setValue(
172 1
            '<a href="'.$this->ReceiptURL.'" target="_blank" class="cms-panel-link action external-link">view</a>'
173
        );
174
175 1
        return $obj;
176
    }
177
178
    /**
179
     * @return string|bool
180
     */
181 49
    public function getDecryptedResponse()
182
    {
183 49
        $decrypted = urldecode($this->Response);
184 49
        if (FoxyCart::getStoreKey()) {
185
            return \rc4crypt::decrypt(FoxyCart::getStoreKey(), $decrypted);
0 ignored issues
show
Bug introduced by
The type rc4crypt was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
186
        }
187 49
        return false;
188
    }
189
190
    /**
191
     * @throws \SilverStripe\ORM\ValidationException
192
     */
193 49
    public function onBeforeWrite()
194
    {
195 49
        $this->parseOrder();
196 49
        parent::onBeforeWrite();
197
    }
198
199
    /**
200
     * @return bool
201
     *
202
     * @throws \SilverStripe\ORM\ValidationException
203
     */
204 49
    public function parseOrder()
205
    {
206 49
        if ($this->getDecryptedResponse()) {
207
            $response = new \SimpleXMLElement($this->getDecryptedResponse());
0 ignored issues
show
Bug introduced by
It seems like $this->getDecryptedResponse() can also be of type true; however, parameter $data of SimpleXMLElement::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

207
            $response = new \SimpleXMLElement(/** @scrutinizer ignore-type */ $this->getDecryptedResponse());
Loading history...
208
209
            $this->parseOrderInfo($response);
210
            $this->parseOrderCustomer($response);
211
            $this->parseOrderDetails($response);
212
213
            return true;
214
        } else {
215 49
            return false;
216
        }
217
    }
218
219
    /**
220
     * @param $response
221
     */
222
    public function parseOrderInfo($response)
223
    {
224
        foreach ($response->transactions->transaction as $transaction) {
225
            // Record transaction data from FoxyCart Datafeed:
226
            $this->Store_ID = (int) $transaction->store_id;
0 ignored issues
show
Bug Best Practice introduced by
The property Store_ID does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
227
            $this->TransactionDate = (string) $transaction->transaction_date;
0 ignored issues
show
Documentation Bug introduced by
It seems like (string)$transaction->transaction_date of type string is incompatible with the declared type SilverStripe\ORM\FieldType\DBDatetime of property $TransactionDate.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
228
            $this->ProductTotal = (float) $transaction->product_total;
0 ignored issues
show
Documentation Bug introduced by
It seems like (double)$transaction->product_total of type double is incompatible with the declared type SilverStripe\ORM\FieldType\DBCurrency of property $ProductTotal.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
229
            $this->TaxTotal = (float) $transaction->tax_total;
0 ignored issues
show
Documentation Bug introduced by
It seems like (double)$transaction->tax_total of type double is incompatible with the declared type SilverStripe\ORM\FieldType\DBCurrency of property $TaxTotal.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
230
            $this->ShippingTotal = (float) $transaction->shipping_total;
0 ignored issues
show
Documentation Bug introduced by
It seems like (double)$transaction->shipping_total of type double is incompatible with the declared type SilverStripe\ORM\FieldType\DBCurrency of property $ShippingTotal.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
231
            $this->OrderTotal = (float) $transaction->order_total;
0 ignored issues
show
Documentation Bug introduced by
It seems like (double)$transaction->order_total of type double is incompatible with the declared type SilverStripe\ORM\FieldType\DBCurrency of property $OrderTotal.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
232
            $this->ReceiptURL = (string) $transaction->receipt_url;
0 ignored issues
show
Documentation Bug introduced by
It seems like (string)$transaction->receipt_url of type string is incompatible with the declared type SilverStripe\ORM\FieldType\DBVarchar of property $ReceiptURL.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
233
            $this->OrderStatus = (string) $transaction->status;
0 ignored issues
show
Documentation Bug introduced by
It seems like (string)$transaction->status of type string is incompatible with the declared type SilverStripe\ORM\FieldType\DBVarchar of property $OrderStatus.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
234
235
            $this->extend('handleOrderInfo', $order, $response);
236
        }
237
    }
238
239
    /**
240
     * @param $response
241
     * @throws \SilverStripe\ORM\ValidationException
242
     */
243
    public function parseOrderCustomer($response)
244
    {
245
        foreach ($response->transactions->transaction as $transaction) {
246
            // if not a guest transaction in FoxyCart
247
            if (isset($transaction->customer_email) && $transaction->is_anonymous == 0) {
248
                // if Customer is existing member, associate with current order
249
                if (Member::get()->filter('Email', $transaction->customer_email)->First()) {
250
                    $customer = Member::get()->filter('Email', $transaction->customer_email)->First();
251
                    // if new customer, create account with data from FoxyCart
252
                } else {
253
                    // set PasswordEncryption to 'none' so imported, encrypted password is not encrypted again
254
                    Config::modify()->set(Security::class, 'password_encryption_algorithm', 'none');
255
256
                    // create new Member, set password info from FoxyCart
257
                    $customer = Member::create();
258
                    $customer->Customer_ID = (int) $transaction->customer_id;
259
                    $customer->FirstName = (string) $transaction->customer_first_name;
260
                    $customer->Surname = (string) $transaction->customer_last_name;
261
                    $customer->Email = (string) $transaction->customer_email;
262
                    $customer->Password = (string) $transaction->customer_password;
263
                    $customer->Salt = (string) $transaction->customer_password_salt;
264
                    $customer->PasswordEncryption = 'none';
265
266
                    // record member record
267
                    $customer->write();
268
                }
269
270
                // set Order MemberID
271
                $this->MemberID = $customer->ID;
272
273
                $this->extend('handleOrderCustomer', $order, $response, $customer);
274
            }
275
        }
276
    }
277
278
    /**
279
     * @param $response
280
     *
281
     * @throws \SilverStripe\ORM\ValidationException
282
     */
283
    public function parseOrderDetails($response)
284
    {
285
286
        // remove previous OrderDetails and OrderOptions so we don't end up with duplicates
287
        foreach ($this->Details() as $detail) {
288
            /** @var OrderOption $orderOption */
289
            foreach ($detail->OrderOptions() as $orderOption) {
290
                $orderOption->delete();
291
            }
292
            $detail->delete();
293
        }
294
295
        foreach ($response->transactions->transaction as $transaction) {
296
            // Associate ProductPages, Options, Quantity with Order
297
            foreach ($transaction->transaction_details->transaction_detail as $detail) {
298
                $OrderDetail = OrderDetail::create();
299
300
                $OrderDetail->Quantity = (int) $detail->product_quantity;
301
                $OrderDetail->ProductName = (string) $detail->product_name;
302
                $OrderDetail->ProductCode = (string) $detail->product_code;
303
                $OrderDetail->ProductImage = (string) $detail->image;
304
                $OrderDetail->ProductCategory = (string) $detail->category_code;
305
                $priceModifier = 0;
306
307
                // parse OrderOptions
308
                foreach ($detail->transaction_detail_options->transaction_detail_option as $option) {
309
                    // Find product via product_id custom variable
310
                    if ($option->product_option_name == 'product_id') {
311
                        // if product is found, set relation to OrderDetail
312
                        $OrderProduct = ProductPage::get()->byID((int) $option->product_option_value);
313
                        if ($OrderProduct) {
314
                            $OrderDetail->ProductID = $OrderProduct->ID;
315
                        }
316
                    } else {
317
                        $OrderOption = OrderOption::create();
318
                        $OrderOption->Name = (string) $option->product_option_name;
319
                        $OrderOption->Value = (string) $option->product_option_value;
320
                        $OrderOption->write();
321
                        $OrderDetail->OrderOptions()->add($OrderOption);
322
323
                        $priceModifier += $option->price_mod;
324
                    }
325
                }
326
327
                $OrderDetail->Price = (float) $detail->product_price + (float) $priceModifier;
328
329
                // extend OrderDetail parsing, allowing for recording custom fields from FoxyCart
330
                $this->extend('handleOrderItem', $order, $response, $OrderDetail);
331
332
                // write
333
                $OrderDetail->write();
334
335
                // associate with this order
336
                $this->Details()->add($OrderDetail);
337
            }
338
        }
339
    }
340
341
    /**
342
     * @param bool $member
343
     *
344
     * @return bool|int
345
     */
346 49
    public function canView($member = null)
347
    {
348 49
        return Permission::check('Product_ORDERS', 'any', $member);
0 ignored issues
show
Bug introduced by
It seems like $member can also be of type boolean; however, parameter $member of SilverStripe\Security\Permission::check() does only seem to accept SilverStripe\Security\Member|integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

348
        return Permission::check('Product_ORDERS', 'any', /** @scrutinizer ignore-type */ $member);
Loading history...
349
    }
350
351
    /**
352
     * @param null $member
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $member is correct as it would always require null to be passed?
Loading history...
353
     *
354
     * @return bool
355
     */
356 1
    public function canEdit($member = null)
357
    {
358 1
        return false;
359
        //return Permission::check('Product_ORDERS', 'any', $member);
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
360
    }
361
362
    /**
363
     * @param null $member
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $member is correct as it would always require null to be passed?
Loading history...
364
     *
365
     * @return bool
366
     */
367 1
    public function canDelete($member = null)
368
    {
369 1
        return false;
370
        //return Permission::check('Product_ORDERS', 'any', $member);
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
371
    }
372
373
    /**
374
     * @param null  $member
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $member is correct as it would always require null to be passed?
Loading history...
375
     * @param array $context
376
     *
377
     * @return bool
378
     */
379 1
    public function canCreate($member = null, $context = [])
380
    {
381 1
        return false;
382
    }
383
384
    /**
385
     * @return array
386
     */
387 1
    public function providePermissions()
388
    {
389
        return array(
390 1
            'Product_ORDERS' => 'Allow user to manage Orders and related objects',
391
        );
392
    }
393
}
394