Completed
Push — master ( cfffae...fa5fd4 )
by Nic
05:44
created

Order::setTransaction()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 0
dl 0
loc 9
ccs 4
cts 5
cp 0.8
crap 2.032
rs 10
c 0
b 0
f 0
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.Nice',
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 = '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 bool|string
180
     */
181 9
    public function getDecryptedResponse()
182
    {
183 9
        $decrypted = urldecode($this->Response);
184 9
        if (FoxyCart::getStoreKey()) {
185
            return \rc4crypt::decrypt(FoxyCart::getStoreKey(), $decrypted);
0 ignored issues
show
Bug introduced by
It seems like Dynamic\FoxyStripe\Model\FoxyCart::getStoreKey() can also be of type false; however, parameter $pwd of rc4crypt::decrypt() 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

185
            return \rc4crypt::decrypt(/** @scrutinizer ignore-type */ FoxyCart::getStoreKey(), $decrypted);
Loading history...
186
        }
187 9
        return false;
188
    }
189
190
    /**
191
     * @throws \SilverStripe\ORM\ValidationException
192
     */
193 9
    public function onBeforeWrite()
194
    {
195 9
        $this->parseOrder();
196 9
        parent::onBeforeWrite();
197
    }
198
199
    /**
200
     * @return bool
201
     *
202
     * @throws \SilverStripe\ORM\ValidationException
203
     */
204 9
    public function parseOrder()
205
    {
206 9
        if ($this->getDecryptedResponse()) {
207
            $response = new \SimpleXMLElement($this->getDecryptedResponse());
208
209
            $this->parseOrderInfo($response);
210
            $this->parseOrderCustomer($response);
211
            $this->parseOrderDetails($response);
212
213
            return true;
214
        } else {
215 9
            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 10
    public function canView($member = null)
347
    {
348 10
        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