Passed
Pull Request — master (#309)
by Jason
06:15 queued 01:51
created

Order::onBeforeWrite()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
namespace Dynamic\FoxyStripe\Model;
4
5
use Dynamic\FoxyStripe\Page\ProductPage;
6
use SilverStripe\Core\Config\Config;
7
use SilverStripe\ORM\DataObject;
8
use SilverStripe\ORM\FieldType\DBHTMLVarchar;
9
use SilverStripe\Security\Member;
10
use SilverStripe\Security\Permission;
11
use SilverStripe\Security\PermissionProvider;
12
13
class Order extends DataObject implements PermissionProvider
14
{
15
    /**
16
     * @var array
17
     */
18
    private static $db = array(
0 ignored issues
show
introduced by
The private property $db is not used, and could be removed.
Loading history...
19
        'Order_ID' => 'Int',
20
        'TransactionDate' => 'Datetime',
21
        'ProductTotal' => 'Currency',
22
        'TaxTotal' => 'Currency',
23
        'ShippingTotal' => 'Currency',
24
        'OrderTotal' => 'Currency',
25
        'ReceiptURL' => 'Varchar(255)',
26
        'OrderStatus' => 'Varchar(255)',
27
        'Response' => 'Text',
28
    );
29
30
    /**
31
     * @var array
32
     */
33
    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...
34
        'Member' => 'Member',
35
    );
36
37
    /**
38
     * @var array
39
     */
40
    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...
41
        'Details' => 'OrderDetail',
42
    );
43
44
    /**
45
     * @var string
46
     */
47
    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...
48
49
    /**
50
     * @var string
51
     */
52
    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...
53
54
    /**
55
     * @var string
56
     */
57
    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...
58
59
    /**
60
     * @var string
61
     */
62
    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...
63
64
    /**
65
     * @var array
66
     */
67
    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...
68
        'Order_ID',
69
        'TransactionDate.NiceUS',
70
        'Member.Name',
71
        'ProductTotal.Nice',
72
        'ShippingTotal.Nice',
73
        'TaxTotal.Nice',
74
        'OrderTotal.Nice',
75
        'ReceiptLink',
76
    );
77
78
    /**
79
     * @var array
80
     */
81
    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...
82
        'Order_ID',
83
        'TransactionDate' => array(
84
            'field' => 'DateField',
85
            'filter' => 'PartialMatchFilter',
86
        ),
87
        'Member.ID',
88
        'OrderTotal',
89
        'Details.ProductID',
90
    );
91
92
    /**
93
     * @var array
94
     */
95
    private static $casting = array(
0 ignored issues
show
introduced by
The private property $casting is not used, and could be removed.
Loading history...
96
        'ReceiptLink' => 'HTMLVarchar',
97
    );
98
99
    /**
100
     * @var array
101
     */
102
    private static $indexes = array(
0 ignored issues
show
introduced by
The private property $indexes is not used, and could be removed.
Loading history...
103
        'Order_ID' => true, // make unique
104
    );
105
106
    /**
107
     * @var string
108
     */
109
    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...
110
111
    /**
112
     * @param bool $includerelations
113
     *
114
     * @return array|string
115
     */
116 1
    public function fieldLabels($includerelations = true)
117
    {
118 1
        $labels = parent::fieldLabels();
119
120 1
        $labels['Order_ID'] = _t('Order.Order_ID', 'Order ID#');
121 1
        $labels['TransactionDate'] = _t('Order.TransactionDate', 'Date');
122 1
        $labels['TransactionDate.NiceUS'] = _t('Order.TransactionDate', 'Date');
123 1
        $labels['Member.Name'] = _t('Order.MemberName', 'Customer');
124 1
        $labels['Member.ID'] = _t('Order.MemberName', 'Customer');
125 1
        $labels['ProductTotal.Nice'] = _t('Order.ProductTotal', 'Sub Total');
126 1
        $labels['TaxTotal.Nice'] = _t('Order.TaxTotal', 'Tax');
127 1
        $labels['ShippingTotal.Nice'] = _t('Order.ShippingTotal', 'Shipping');
128 1
        $labels['OrderTotal'] = _t('Order.OrderTotal', 'Total');
129 1
        $labels['OrderTotal.Nice'] = _t('Order.OrderTotal', 'Total');
130 1
        $labels['ReceiptLink'] = _t('Order.ReceiptLink', 'Invoice');
131 1
        $labels['Details.ProductID'] = _t('Order.Details.ProductID', 'Product');
132
133 1
        return $labels;
134
    }
135
136
    /**
137
     * @return mixed
138
     */
139 1
    public function ReceiptLink()
140
    {
141 1
        return $this->getReceiptLink();
142
    }
143
144
    /**
145
     * @return mixed
146
     */
147 1
    public function getReceiptLink()
148
    {
149 1
        $obj = DBHTMLVarchar::create();
150 1
        $obj->setValue('<a href="'.$this->ReceiptURL.'" target="_blank" class="cms-panel-link action external-link">view</a>');
151
152 1
        return $obj;
153
    }
154
155
    /**
156
     * @return mixed
157
     */
158 49
    public function getDecryptedResponse()
159
    {
160 49
        $decrypted = urldecode($this->Response);
0 ignored issues
show
Bug Best Practice introduced by
The property Response does not exist on Dynamic\FoxyStripe\Model\Order. Since you implemented __get, consider adding a @property annotation.
Loading history...
161 49
        if (FoxyCart::getStoreKey()) {
162
            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...
163
        }
164
    }
165
166
    /**
167
     * @throws \SilverStripe\ORM\ValidationException
168
     */
169 49
    public function onBeforeWrite()
170
    {
171 49
        $this->parseOrder();
172 49
        parent::onBeforeWrite();
173
    }
174
175
    /**
176
     * @return bool
177
     *
178
     * @throws \SilverStripe\ORM\ValidationException
179
     */
180 49
    public function parseOrder()
181
    {
182 49
        if ($this->getDecryptedResponse()) {
183
            $response = new \SimpleXMLElement($this->getDecryptedResponse());
184
185
            $this->parseOrderInfo($response);
186
            $this->parseOrderCustomer($response);
187
            $this->parseOrderDetails($response);
188
189
            return true;
190
        } else {
191 49
            return false;
192
        }
193
    }
194
195
    /**
196
     * @param $response
197
     */
198
    public function parseOrderInfo($response)
199
    {
200
        foreach ($response->transactions->transaction as $transaction) {
201
202
            // Record transaction data from FoxyCart Datafeed:
203
            $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...
204
            $this->TransactionDate = (string) $transaction->transaction_date;
0 ignored issues
show
Bug Best Practice introduced by
The property TransactionDate does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
205
            $this->ProductTotal = (float) $transaction->product_total;
0 ignored issues
show
Bug Best Practice introduced by
The property ProductTotal does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
206
            $this->TaxTotal = (float) $transaction->tax_total;
0 ignored issues
show
Bug Best Practice introduced by
The property TaxTotal does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
207
            $this->ShippingTotal = (float) $transaction->shipping_total;
0 ignored issues
show
Bug Best Practice introduced by
The property ShippingTotal does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
208
            $this->OrderTotal = (float) $transaction->order_total;
0 ignored issues
show
Bug Best Practice introduced by
The property OrderTotal does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
209
            $this->ReceiptURL = (string) $transaction->receipt_url;
0 ignored issues
show
Bug Best Practice introduced by
The property ReceiptURL does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
210
            $this->OrderStatus = (string) $transaction->status;
0 ignored issues
show
Bug Best Practice introduced by
The property OrderStatus does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
211
212
            $this->extend('handleOrderInfo', $order, $response);
213
        }
214
    }
215
216
    /**
217
     * @param $response
218
     */
219
    public function parseOrderCustomer($response)
220
    {
221
        foreach ($response->transactions->transaction as $transaction) {
222
223
            // if not a guest transaction in FoxyCart
224
            if (isset($transaction->customer_email) && $transaction->is_anonymous == 0) {
225
226
                // if Customer is existing member, associate with current order
227
                if (Member::get()->filter('Email', $transaction->customer_email)->First()) {
228
                    $customer = Member::get()->filter('Email', $transaction->customer_email)->First();
229
230
                    // if new customer, create account with data from FoxyCart
231
                } else {
232
233
                    // set PasswordEncryption to 'none' so imported, encrypted password is not encrypted again
234
                    Config::inst()->update('Security', 'password_encryption_algorithm', 'none');
0 ignored issues
show
Bug introduced by
The method update() does not exist on SilverStripe\Config\Coll...nfigCollectionInterface. It seems like you code against a sub-type of SilverStripe\Config\Coll...nfigCollectionInterface such as SilverStripe\Config\Coll...\MemoryConfigCollection. ( Ignorable by Annotation )

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

234
                    Config::inst()->/** @scrutinizer ignore-call */ update('Security', 'password_encryption_algorithm', 'none');
Loading history...
235
236
                    // create new Member, set password info from FoxyCart
237
                    $customer = Member::create();
238
                    $customer->Customer_ID = (int) $transaction->customer_id;
239
                    $customer->FirstName = (string) $transaction->customer_first_name;
240
                    $customer->Surname = (string) $transaction->customer_last_name;
241
                    $customer->Email = (string) $transaction->customer_email;
242
                    $customer->Password = (string) $transaction->customer_password;
243
                    $customer->Salt = (string) $transaction->customer_password_salt;
244
                    $customer->PasswordEncryption = 'none';
245
246
                    // record member record
247
                    $customer->write();
248
                }
249
250
                // set Order MemberID
251
                $this->MemberID = $customer->ID;
0 ignored issues
show
Bug Best Practice introduced by
The property MemberID does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
252
253
                $this->extend('handleOrderCustomer', $order, $response, $customer);
254
            }
255
        }
256
    }
257
258
    /**
259
     * @param $response
260
     *
261
     * @throws \SilverStripe\ORM\ValidationException
262
     */
263
    public function parseOrderDetails($response)
264
    {
265
266
        // remove previous OrderDetails and OrderOptions so we don't end up with duplicates
267
        foreach ($this->Details() as $detail) {
0 ignored issues
show
Bug introduced by
The method Details() does not exist on Dynamic\FoxyStripe\Model\Order. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

267
        foreach ($this->/** @scrutinizer ignore-call */ Details() as $detail) {
Loading history...
268
            foreach ($detail->OrderOptions() as $orderOption) {
269
                $orderOption->delete();
270
            }
271
            $detail->delete();
272
        }
273
274
        foreach ($response->transactions->transaction as $transaction) {
275
276
            // Associate ProductPages, Options, Quantity with Order
277
            foreach ($transaction->transaction_details->transaction_detail as $detail) {
278
                $OrderDetail = OrderDetail::create();
279
280
                $OrderDetail->Quantity = (int) $detail->product_quantity;
281
                $OrderDetail->ProductName = (string) $detail->product_name;
282
                $OrderDetail->ProductCode = (string) $detail->product_code;
283
                $OrderDetail->ProductImage = (string) $detail->image;
284
                $OrderDetail->ProductCategory = (string) $detail->category_code;
285
                $priceModifier = 0;
286
287
                // parse OrderOptions
288
                foreach ($detail->transaction_detail_options->transaction_detail_option as $option) {
289
290
                    // Find product via product_id custom variable
291
                    if ($option->product_option_name == 'product_id') {
292
293
                        // if product is found, set relation to OrderDetail
294
                        $OrderProduct = ProductPage::get()->byID((int) $option->product_option_value);
295
                        if ($OrderProduct) {
296
                            $OrderDetail->ProductID = $OrderProduct->ID;
297
                        }
298
                    } else {
299
                        $OrderOption = OrderOption::create();
300
                        $OrderOption->Name = (string) $option->product_option_name;
301
                        $OrderOption->Value = (string) $option->product_option_value;
302
                        $OrderOption->write();
303
                        $OrderDetail->OrderOptions()->add($OrderOption);
304
305
                        $priceModifier += $option->price_mod;
306
                    }
307
                }
308
309
                $OrderDetail->Price = (float) $detail->product_price + (float) $priceModifier;
310
311
                // extend OrderDetail parsing, allowing for recording custom fields from FoxyCart
312
                $this->extend('handleOrderItem', $order, $response, $OrderDetail);
313
314
                // write
315
                $OrderDetail->write();
316
317
                // associate with this order
318
                $this->Details()->add($OrderDetail);
319
            }
320
        }
321
    }
322
323
    /**
324
     * @param bool $member
325
     *
326
     * @return bool|int
327
     */
328 49
    public function canView($member = false)
329
    {
330 49
        return Permission::check('Product_ORDERS', 'any', $member);
0 ignored issues
show
Bug introduced by
$member of type boolean is incompatible with the type SilverStripe\Security\Member|integer expected by parameter $member of SilverStripe\Security\Permission::check(). ( Ignorable by Annotation )

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

330
        return Permission::check('Product_ORDERS', 'any', /** @scrutinizer ignore-type */ $member);
Loading history...
331
    }
332
333
    /**
334
     * @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...
335
     *
336
     * @return bool
337
     */
338 1
    public function canEdit($member = null)
339
    {
340 1
        return false;
341
        //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...
342
    }
343
344
    /**
345
     * @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...
346
     *
347
     * @return bool
348
     */
349 1
    public function canDelete($member = null)
350
    {
351 1
        return false;
352
        //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...
353
    }
354
355
    /**
356
     * @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...
357
     * @param array $context
358
     *
359
     * @return bool
360
     */
361 1
    public function canCreate($member = null, $context = [])
362
    {
363 1
        return false;
364
    }
365
366
    /**
367
     * @return array
368
     */
369 1
    public function providePermissions()
370
    {
371
        return array(
372 1
            'Product_ORDERS' => 'Allow user to manage Orders and related objects',
373
        );
374
    }
375
}
376