Test Failed
Push — master ( e05308...65ef93 )
by Alexey
04:19
created

Cart::calc()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 1
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Cart
5
 *
6
 * @author Alexey Krupskiy <[email protected]>
7
 * @link http://inji.ru/
8
 * @copyright 2015 Alexey Krupskiy
9
 * @license https://github.com/injitools/cms-Inji/blob/master/LICENSE
10
 */
11
12
namespace Ecommerce;
13
/**
14
 * Class Cart
15
 *
16
 * @property int $id
17
 * @property int $user_id
18
 * @property int $cart_status_id
19
 * @property int $delivery_id
20
 * @property int $paytype_id
21
 * @property int $card_item_id
22
 * @property bool $warehouse_block
23
 * @property bool $payed
24
 * @property string $comment
25
 * @property bool $exported
26
 * @property string $complete_data
27
 * @property string $date_status
28
 * @property string $date_last_activ
29
 * @property string $date_create
30
 *
31
 * @property-read \Users\User $user
32
 * @property-read \Ecommerce\Cart\Item[] $cartItems
33
 * @property-read \Ecommerce\Cart\Event[] $events
34
 * @property-read \Ecommerce\Cart\Status $status
35
 * @property-read \Ecommerce\Delivery $delivery
36
 * @property-read \Ecommerce\PayType $payType
37
 * @property-read \Ecommerce\Cart\Info[] $infos
38
 * @property-read \Ecommerce\Cart\DeliveryInfo[] $deliveryInfos
39
 * @property-read \Ecommerce\Cart\Extra[] $extras
40
 * @property-read \Ecommerce\Card\Item $card
41
 * @property-read \Money\Pay[] $pays
42
 * @property-read \Ecommerce\Cart\Discount[] $discounts
43
 *
44
 * @method \Users\User user($options)($options)
45
 * @method \Ecommerce\Cart\Item[] cartItems($options)
46
 * @method \Ecommerce\Cart\Event[] events($options)
47
 * @method \Ecommerce\Cart\Status status($options)
48
 * @method \Ecommerce\Delivery delivery($options)
49
 * @method \Ecommerce\PayType payType($options)
50
 * @method \Ecommerce\Cart\Info[] infos($options)
51
 * @method \Ecommerce\Cart\DeliveryInfo[] deliveryInfos($options)
52
 * @method \Ecommerce\Cart\Extra[] extras($options)
53
 * @method \Ecommerce\Card\Item card($options)
54
 * @method \Money\Pay[] pays($options)
55
 * @method \Ecommerce\Cart\Discount[] discounts($options)
56
 */
57
class Cart extends \Model {
58
59
    public static $objectName = 'Корзины';
60
61
    public static function indexes() {
62
        return [
63
            'ecommerce_cartStatusBlock' => [
64
                'type' => 'INDEX',
65
                'cols' => [
66
                    'cart_cart_status_id',
67
                    'cart_warehouse_block'
68
                ]
69
            ],
70
            'ecommerce_cartStats' => [
71
                'type' => 'INDEX',
72
                'cols' => [
73
                    'cart_cart_status_id',
74
                ]
75
            ],
76
            'ecommerce_cartBlock' => [
77
                'type' => 'INDEX',
78
                'cols' => [
79
                    'cart_warehouse_block'
80
                ]
81
            ],
82
        ];
83
    }
84
85
    public static function relations() {
86
        return [
87
            'user' => [
88
                'model' => 'Users\User',
89
                'col' => 'user_id'
90
            ],
91
            'cartItems' => [
92
                'type' => 'many',
93
                'model' => 'Ecommerce\Cart\Item',
94
                'col' => 'cart_id',
95
            ],
96
            'events' => [
97
                'type' => 'many',
98
                'model' => 'Ecommerce\Cart\Event',
99
                'col' => 'cart_id',
100
            ],
101
            'status' => [
102
                'model' => 'Ecommerce\Cart\Status',
103
                'col' => 'cart_status_id'
104
            ],
105
            'delivery' => [
106
                'model' => 'Ecommerce\Delivery',
107
                'col' => 'delivery_id'
108
            ],
109
            'payType' => [
110
                'model' => 'Ecommerce\PayType',
111
                'col' => 'paytype_id'
112
            ],
113
            'infos' => [
114
                'type' => 'many',
115
                'model' => 'Ecommerce\Cart\Info',
116
                'col' => 'cart_id',
117
                'resultKey' => 'useradds_field_id'
118
            ],
119
            'deliveryInfos' => [
120
                'type' => 'many',
121
                'model' => 'Ecommerce\Cart\DeliveryInfo',
122
                'col' => 'cart_id',
123
                'resultKey' => 'delivery_field_id'
124
            ],
125
            'extras' => [
126
                'type' => 'many',
127
                'model' => 'Ecommerce\Cart\Extra',
128
                'col' => 'cart_id'
129
            ],
130
            'card' => [
131
                'model' => 'Ecommerce\Card\Item',
132
                'col' => 'card_item_id'
133
            ],
134
            'pays' => [
135
                'type' => 'many',
136
                'model' => 'Money\Pay',
137
                'col' => 'data'
138
            ],
139
            'discounts' => [
140
                'type' => 'relModel',
141
                'relModel' => 'Ecommerce\Cart\Discount',
142
                'model' => 'Ecommerce\Discount',
143
            ]
144
        ];
145
    }
146
147
    public function beforeDelete() {
148
        foreach ($this->cartItems as $cartItem) {
149
            $cartItem->delete();
150
        }
151
        foreach ($this->infos as $info) {
152
            $info->delete();
153
        }
154
        foreach ($this->extras as $extra) {
155
            $extra->delete();
156
        }
157
        foreach ($this->events as $event) {
158
            $event->delete();
159
        }
160
    }
161
162
    public static $labels = [
163
        'user_id' => 'Пользователь',
164
        'cart_status_id' => 'Статус',
165
        'delivery_id' => 'Доставка',
166
        'comment' => 'Комментарий',
167
        'bonus_used' => 'Выгодные рубли',
168
        'complete_data' => 'Время заказа',
169
        'info' => 'Информация',
170
        'items' => 'Товары',
171
        'paytype_id' => 'Способ оплаты',
172
        'payed' => 'Оплачен',
173
        'exported' => 'Выгружено',
174
        'warehouse_block' => 'Блокировка товаров',
175
        'extra' => 'Доп.',
176
        'card_item_id' => 'Дисконтная карта',
177
        'info' => 'Информация',
178
        'contacts' => 'Информация',
179
        'pay' => 'Счета',
180
        'sums' => 'Суммы',
181
        'deliveryInfo' => 'Для доставки',
182
        'discount' => 'Скидки',
183
    ];
184
    public static $cols = [
185
        //Основные параметры
186
        'user_id' => ['type' => 'select', 'source' => 'relation', 'relation' => 'user'],
187
        'cart_status_id' => ['type' => 'select', 'source' => 'relation', 'relation' => 'status'],
188
        'delivery_id' => ['type' => 'select', 'source' => 'relation', 'relation' => 'delivery'],
189
        'paytype_id' => ['type' => 'select', 'source' => 'relation', 'relation' => 'payType'],
190
        'card_item_id' => ['type' => 'select', 'source' => 'relation', 'relation' => 'card'],
191
        'warehouse_block' => ['type' => 'bool'],
192
        'payed' => ['type' => 'bool'],
193
        'comment' => ['type' => 'textarea'],
194
        //Системные
195
        'exported' => ['type' => 'bool'],
196
        'complete_data' => ['type' => 'dateTime', 'null' => true, 'emptyValue' => null],
197
        'date_status' => ['type' => 'dateTime', 'null' => true, 'emptyValue' => null],
198
        'date_last_activ' => ['type' => 'dateTime', 'null' => true, 'emptyValue' => null, 'logging' => false],
199
        'date_create' => ['type' => 'dateTime'],
200
        //Виджеты
201
        'sums' => [
202
            'type' => 'void',
203
            'view' => [
204
                'type' => 'widget',
205
                'widget' => 'Ecommerce\adminSums',
206
            ],
207
        ],
208
        'contacts' => [
209
            'type' => 'void',
210
            'view' => [
211
                'type' => 'widget',
212
                'widget' => 'Ecommerce\admin/contacts',
213
            ],
214
        ],
215
        //Менеджеры
216
        'extra' => ['type' => 'dataManager', 'relation' => 'extras'],
217
        'pay' => ['type' => 'dataManager', 'relation' => 'pays'],
218
        'items' => ['type' => 'dataManager', 'relation' => 'cartItems'],
219
        'info' => ['type' => 'dataManager', 'relation' => 'infos'],
220
        'deliveryInfo' => ['type' => 'dataManager', 'relation' => 'deliveryInfos'],
221
        'discount' => ['type' => 'dataManager', 'relation' => 'discounts'],
222
    ];
223
    public static $dataManagers = [
224
        'manager' => [
225
            'cols' => [
226
                'contacts',
227
                'items',
228
                'extra',
229
                'discount',
230
                'sums',
231
                'cart_status_id',
232
                'delivery_id',
233
                'deliveryInfo',
234
                'payed',
235
                'pay',
236
                'complete_data',
237
            ],
238
            'sortable' => [
239
                'cart_status_id',
240
                'delivery_id',
241
                'payed',
242
                'complete_data',
243
            ],
244
            'filters' => [
245
                'cart_status_id',
246
                'delivery_id',
247
                'payed',
248
                'complete_data',
249
            ],
250
            'preSort' => [
251
                'complete_data' => 'desc'
252
            ],
253
            'actions' => [
254
                'Ecommerce\CloseCartBtn', 'Open', 'Edit', 'Delete'
255
            ]
256
        ]
257
    ];
258
259
    public static function itemName($item) {
260
        return $item->pk() . '. ' . $item->name();
261
    }
262
263
    public static $forms = [
264
        'manager' => [
265
            'inputs' => [
266
                'userSearch' => [
267
                    'type' => 'search',
268
                    'source' => 'relation',
269
                    'relation' => 'user',
270
                    'label' => 'Покупатель',
271
                    'cols' => [
272
                        'info:first_name',
273
                        'info:last_name',
274
                        'info:middle_name',
275
                        'mail'
276
                    ],
277
                    'col' => 'user_id',
278
                    'required' => true,
279
                    'showCol' => [
280
                        'type' => 'staticMethod',
281
                        'class' => 'Ecommerce\Cart',
282
                        'method' => 'itemName',
283
                    ],
284
                ],
285
                'cardSearch' => [
286
                    'type' => 'search',
287
                    'source' => 'relation',
288
                    'relation' => 'card',
289
                    'label' => 'Дисконтная карта',
290
                    'cols' => [
291
                        'code',
292
                        'user:info:first_name',
293
                        'user:info:last_name',
294
                        'user:info:middle_name',
295
                        'user:mail'
296
                    ],
297
                    'col' => 'card_item_id',
298
                ],
299
            ],
300
            'map' => [
301
                ['userSearch', 'cart_status_id'],
302
                ['paytype_id', 'delivery_id'],
303
                ['cardSearch', 'comment'],
304
                ['warehouse_block', 'complete_data'],
305
                ['payed'],
306
                ['items'],
307
                ['extra'],
308
                ['pay'],
309
                ['info'],
310
                ['deliveryInfo']
311
            ]
312
        ],
313
    ];
314
315
    public function buildOrderInfo() {
316
        $orderInfo = '<h3>Товары</h3>';
317
        $orderInfo .= '<table cellspacing="2" border="1" cellpadding="5"><tr><th>Товар</th><th>Артикул</th><th>Кол-во</th><th>Цена</th><th>Сумма</th></tr>';
318
        foreach ($this->cartItems as $cartItem) {
319
            $orderInfo .= "<tr>";
320
            $orderInfo .= "<td><a href='" . \App::$cur->getDomain() . "{$cartItem->item->getHref()}'>{$cartItem->name()}</a></td>";
321
            $orderInfo .= "<td>{$cartItem->price->offer->article}</td>";
322
            $orderInfo .= "<td>{$cartItem->count}</td>";
323
            $orderInfo .= "<td>{$cartItem->final_price}</td>";
324
            $orderInfo .= "<td>" . ($cartItem->final_price * $cartItem->count) . "</td>";
325
            $orderInfo .= "</tr>";
326
        }
327
        $orderInfo .= '</table>';
328 View Code Duplication
        if ($this->infos) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->infos of type Ecommerce\Cart\Info[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
329
            $orderInfo .= '<h3>Контакты</h3>';
330
            $orderInfo .= '<table cellspacing="2" border="1" cellpadding="5">';
331
            $orderInfo .= "<tr><td>E-mail</td><td><b>{$this->user->mail}</b></td></tr>";
332
            foreach ($this->infos as $info) {
333
                $value = \Model::resloveTypeValue($info, 'value');
334
                $orderInfo .= "<tr><td>{$info->name}</td><td><b>{$value}</b></td></tr>";
335
            }
336
            $orderInfo .= '</table>';
337
        }
338 View Code Duplication
        if ($this->delivery) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
339
            $orderInfo .= '<h3>Информация о доставке</h3>';
340
            $orderInfo .= "<p><b>{$this->delivery->name}</b></p>";
341
            $orderInfo .= '<table cellspacing="2" border="1" cellpadding="5">';
342
            foreach ($this->deliveryInfos as $info) {
343
                $value = \Model::resloveTypeValue($info, 'value');
344
                $orderInfo .= "<tr><td>{$info->name}</td><td><b>{$value}</b></td></tr>";
345
            }
346
            $orderInfo .= '</table>';
347
        }
348
        if ($this->payType) {
349
            $orderInfo .= '<h3>Способ оплаты</h3>';
350
            $orderInfo .= "<p><b>{$this->payType->name}</b></p>";
351
        }
352
        return $orderInfo;
353
    }
354
355
    public function checkStage() {
356
        $sum = $this->itemsSum();
357
        $stages = Cart\Stage::getList(['order' => ['sum', 'asc']]);
358
        $groups = [];
359
        foreach ($stages as $stage) {
360
            if ($sum->greater(new \Money\Sums([$stage->currency_id => $stage->sum])) || $sum->equal(new \Money\Sums([$stage->currency_id => $stage->sum]))) {
361
                $groups[$stage->group] = $stage;
362
            }
363
        }
364
        $discounts = Cart\Discount::getList(['where' => ['cart_id', $this->id]]);
365
        foreach ($discounts as $discount) {
366
            if (!isset($groups[$discount->group]) && $discount->auto) {
367
                $discount->delete();
368
            }
369
            if (isset($groups[$discount->group]) && $groups[$discount->group]->type == 'discount') {
370
                $discount->discount_id = $groups[$discount->group]->value;
371
                $discount->save();
372
                unset($groups[$discount->group]);
373
            }
374
        }
375
        foreach ($groups as $group) {
376
            if ($group && $group->type == 'discount') {
377
                $rel = $this->addRelation('discounts', $group->value);
378
                $rel->auto = true;
379
                $rel->group = 'discount';
380
                $rel->save();
381
            }
382
        }
383
    }
384
385
    public function needDelivery() {
386
        foreach ($this->cartItems as $cartItem) {
387
            if ((!$cartItem->item->type && !empty(\App::$cur->ecommerce->config['defaultNeedDelivery'])) || ($cartItem->item->type && $cartItem->item->type->delivery)) {
388
                return true;
389
            }
390
        }
391
        return false;
392
    }
393
394
    public function deliverySum() {
395
        $sum = new \Money\Sums([0 => 0]);
396
        if ($this->delivery && $this->needDelivery()) {
397
            $sums = $this->itemsSum();
398
            $deliveryPrice = new \Money\Sums([$this->delivery->currency_id => $this->delivery->max_cart_price]);
399
            if ($this->delivery->max_cart_price && $sums->greater($deliveryPrice) || $sums->equal($deliveryPrice)) {
400
                $sum->sums = [$this->delivery->currency_id => 0];
401
            } else if ($this->delivery->prices) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->delivery->prices of type Ecommerce\Delivery\Price[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
402
                foreach ($this->delivery->prices(['order' => ['cart_price', 'asc']]) as $delPrice) {
403
                    $deliveryPrice = new \Money\Sums([$delPrice->currency_id => $delPrice->cart_price]);
404
                    if ($sums->greater($deliveryPrice) || $sums->equal($deliveryPrice)) {
405
                        $sum->sums = [$delPrice->currency_id => $delPrice->price];
406
                    }
407
                }
408
                if (!$sum->sums) {
409
                    $sum->sums = [$this->delivery->currency_id => $this->delivery->price];
410
                }
411
            } else {
412
                if (!$this->delivery->provider) {
0 ignored issues
show
Bug introduced by
The property provider does not seem to exist. Did you mean delivery_provider_id?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
413
                    $sum->sums = [$this->delivery->currency_id => $this->delivery->price];
414
                } else {
415
                    $className = 'Ecommerce\DeliveryProvider\\' . $this->delivery->provider->object;
0 ignored issues
show
Bug introduced by
The property provider does not seem to exist. Did you mean delivery_provider_id?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
416
                    $sum = $className::calcPrice($this);
417
                }
418
            }
419
        }
420
        return $sum;
421
    }
422
423
    public function hasDiscount() {
424
        return (bool) $this->card || $this->discounts;
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->discounts of type Ecommerce\Cart\Discount[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
425
    }
426
427
    public function discountSum() {
428
        $sums = [];
429
        foreach ($this->cartItems as $cartItem) {
430
            $sums[$cartItem->price->currency_id] = isset($sums[$cartItem->price->currency_id]) ? $sums[$cartItem->price->currency_id] + $cartItem->discount() * $cartItem->count : $cartItem->discount() * $cartItem->count;
431
        }
432
        return new \Money\Sums($sums);
433
    }
434
435
    public function finalSum() {
436
        $sums = $this->itemsSum();
437
        $sums = $sums->minus($this->discountSum());
438
        $sums = $sums->plus($this->deliverySum());
439
        return $sums;
440
    }
441
442
    public function itemsSum() {
443
        $cart = Cart::get($this->id);
444
        $sums = [];
445
        foreach ($cart->cartItems as $cartItem) {
446
            if (!$cartItem->price) {
447
                continue;
448
            }
449
            $sums[$cartItem->price->currency_id] = isset($sums[$cartItem->price->currency_id]) ? $sums[$cartItem->price->currency_id] + $cartItem->price->price * $cartItem->count : $cartItem->price->price * $cartItem->count;
450
        }
451
        return new \Money\Sums($sums);
452
    }
453
454
    public function checkPrices() {
455
        $change = false;
456
        foreach ($this->cartItems as $cartItem) {
457
            if ($cartItem->price && !$cartItem->price->checkUserAccess()) {
458
                $newPrice = $cartItem->price->offer->getPrice();
459
                $cartItem->item_offer_price_id = $newPrice->id;
460
                $cartItem->save();
461
                $cartItem->loadRelation('price');
462
                $change = true;
463
            }
464
        }
465
        return $change;
466
    }
467
468
    public function addItem($offer_price_id, $count = 1, $final_price = 0) {
469
        $price = Item\Offer\Price::get((int) $offer_price_id);
470
471
        if (!$price) {
472
            return false;
473
        }
474
475
        if ($count <= 0) {
476
            $count = 1;
477
        }
478
479
        $cartItem = new Cart\Item();
480
        $cartItem->cart_id = $this->id;
481
        $cartItem->item_id = $price->offer->item->id;
482
        $cartItem->count = $count;
483
        $cartItem->item_offer_price_id = $price->id;
484
        $cartItem->final_price = $final_price ? $final_price : $price->price;
485
        $cartItem->save();
486
        return true;
487
    }
488
489
    public function calc($save = true) {
490
        if ($save) {
491
            $this->save();
492
        }
493
    }
494
495
    public function availablePayTypes() {
496
        if (!$this->delivery) {
497
            return \Ecommerce\PayType::getList(['order' => ['weight', 'ASC']]);
498
        }
499
        $providerHelper = $this->delivery->providerHelper();
500
        if (!$providerHelper) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $providerHelper of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false 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...
501
            return \Ecommerce\PayType::getList(['order' => ['weight', 'ASC']]);
502
        }
503
        $payTypeGroups = $providerHelper::availablePayTypeGroups($this);
504
        if (!$payTypeGroups || $payTypeGroups[0] === '*') {
505
            return \Ecommerce\PayType::getList(['order' => ['weight', 'ASC']]);
506
        }
507
        return \Ecommerce\PayType::getList(['where' => ['group', $payTypeGroups, 'IN'], 'order' => ['weight', 'ASC']]);
508
    }
509
}
510