GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — master ( c721cc...3ff35f )
by Ivan
09:59
created

BackendOrderController::actionAutoCompleteSearch()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 24
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 24
rs 8.9713
c 0
b 0
f 0
cc 3
eloc 17
nc 4
nop 3
1
<?php
2
3
namespace app\modules\shop\controllers;
4
5
use app\backend\components\BackendController;
6
use app\backend\models\Notification;
7
use app\backend\models\OrderChat;
8
use app\components\Helper;
9
use app\components\SearchModel;
10
use app\modules\core\helpers\EventSubscribingHelper;
11
use app\modules\shop\events\OrderCalculateEvent;
12
use app\modules\shop\helpers\PriceHelper;
13
use app\modules\shop\models\Contragent;
14
use app\modules\shop\models\Customer;
15
use app\modules\shop\models\DeliveryInformation;
16
use app\modules\shop\models\Order;
17
use app\modules\shop\models\OrderDeliveryInformation;
18
use app\modules\shop\models\OrderItem;
19
use app\modules\shop\models\OrderStage;
20
use app\modules\shop\models\OrderTransaction;
21
use app\modules\shop\models\PaymentType;
22
use app\modules\shop\models\Product;
23
use app\modules\shop\models\ShippingOption;
24
use app\modules\shop\models\SpecialPriceList;
25
use app\modules\shop\ShopModule;
26
use app\modules\user\models\User;
27
use app\properties\HasProperties;
28
use kartik\helpers\Html;
29
use Yii;
30
use yii\caching\TagDependency;
31
use yii\data\ArrayDataProvider;
32
use yii\db\Query;
33
use yii\filters\AccessControl;
34
use yii\helpers\ArrayHelper;
35
use yii\helpers\Url;
36
use yii\web\BadRequestHttpException;
37
use yii\web\NotFoundHttpException;
38
use yii\web\Response;
39
40
/**
41
 * OrderController implements the CRUD actions for Order model.
42
 */
43
class BackendOrderController extends BackendController
44
{
45
    /**
46
     * @property ShopModule $module
47
     */
48
49
    /**
50
     * Finds the Order model based on its primary key value.
51
     * If the model is not found, a 404 HTTP exception will be thrown.
52
     * @param string $id
53
     * @return Order the loaded model
54
     * @throws NotFoundHttpException if the model cannot be found
55
     */
56
    protected function findModel($id)
57
    {
58
        if (($model = Order::findOne($id)) !== null) {
59
            return $model;
60
        } else {
61
            throw new NotFoundHttpException('The requested page does not exist.');
62
        }
63
    }
64
65
    public function actionDownloadFile($key, $orderId)
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...
66
    {
67
        $order = Order::findOne($orderId);
68
        if ($order === null) {
69
            throw new NotFoundHttpException('Order not found');
70
        }
71
        $prop = $order->getPropertyValuesByKey($key);
0 ignored issues
show
Documentation Bug introduced by
The method getPropertyValuesByKey does not exist on object<app\modules\shop\models\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...
72
        if (empty($prop->values) === false) {
73
            $fileName = Yii::getAlias(Yii::$app->getModule('core')->visitorsFileUploadPath) . DIRECTORY_SEPARATOR . ArrayHelper::getValue($prop, 'values.0.value', '');
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 167 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
74
            if (file_exists($fileName)) {
75
                return Yii::$app->response->sendFile($fileName);
76
            }
77
        }
78
        throw new NotFoundHttpException(Yii::t('app', 'File not found'));
79
    }
80
81
82
    protected function getManagersList()
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...
83
    {
84
        $managers = Yii::$app->cache->get('ManagersList');
85
        if ($managers === false) {
86
            $managers = User::find()
87
                ->join(
88
                    'INNER JOIN',
89
                    '{{%auth_assignment}}',
90
                    '{{%auth_assignment}}.user_id = ' . User::tableName() . '.id'
91
                )
92
                ->where(['{{%auth_assignment}}.item_name' => 'manager'])
93
                ->all();
94
            $managers = ArrayHelper::map($managers, 'id', 'username');
95
            Yii::$app->cache->set(
96
                'ManagersList',
97
                $managers,
98
                86400,
99
                new TagDependency(
100
                    [
101
                        'tags' => [
102
                            \devgroup\TagDependencyHelper\ActiveRecordHelper::getCommonTag(User::className())
103
                        ],
104
                    ]
105
                )
106
            );
107
        }
108
        return $managers;
109
    }
110
111
    /**
112
     * @inheritdoc
113
     */
114 View Code Duplication
    public function behaviors()
115
    {
116
        return [
117
            'access' => [
118
                'class' => AccessControl::className(),
119
                'rules' => [
120
                    [
121
                        'allow' => true,
122
                        'roles' => ['order manage'],
123
                    ],
124
                ],
125
            ],
126
        ];
127
    }
128
129
    public function beforeAction($action)
130
    {
131
        if (false === parent::beforeAction($action)) {
132
            return false;
133
        }
134
135
        EventSubscribingHelper::specialEventCallback(OrderCalculateEvent::className(),
136
            function (OrderCalculateEvent $event)
137
            {
138
                if (OrderCalculateEvent::AFTER_CALCULATE !== $event->state) {
139
                    return null;
140
                }
141
142
                /** @var OrderTransaction $transaction */
143
                $transaction = OrderTransaction::findLastByOrder(
144
                    $event->order,
145
                    null,
146
                    false,
147
                    false,
148
                    [OrderTransaction::TRANSACTION_START]
149
                );
150
151
                if (!empty($transaction)) {
152
                    $transaction->total_sum = $event->order->total_price;
153
                    $transaction->save();
154
                }
155
            }
156
        );
157
158
        return true;
159
    }
160
161
162
    /**
163
     * Lists all Order models.
164
     * @return mixed
165
     */
166
    public function actionIndex()
167
    {
168
        $searchModelConfig = [
169
            'defaultOrder' => ['id' => SORT_DESC],
170
            'model' => Order::className(),
171
            'relations' => ['user' => ['username']],
172
            'partialMatchAttributes' => ['start_date', 'end_date', 'user_username'],
173
            'additionalConditions' => [],
174
        ];
175
176
        if (intval($this->module->showDeletedOrders) === 0) {
177
            $searchModelConfig['additionalConditions'] = [['is_deleted' => 0]];
178
        }
179
180
        /** @var SearchModel $searchModel */
181
        $searchModel = new SearchModel($searchModelConfig);
182
        if (intval($this->module->defaultOrderStageFilterBackend) > 0) {
183
            $searchModel->order_stage_id = intval($this->module->defaultOrderStageFilterBackend);
0 ignored issues
show
Documentation introduced by
The property order_stage_id does not exist on object<app\components\SearchModel>. 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...
184
        }
185
        $dataProvider = $searchModel->search(Yii::$app->request->queryParams);
186
        return $this->render(
187
            'index',
188
            [
189
                'dataProvider' => $dataProvider,
190
                'managers' => $this->getManagersList(),
191
                'orderStages' => Helper::getModelMap(OrderStage::className(), 'id', 'name_short'),
192
                'paymentTypes' => Helper::getModelMap(PaymentType::className(), 'id', 'name'),
193
                'searchModel' => $searchModel,
194
                'shippingOptions' => Helper::getModelMap(ShippingOption::className(), 'id', 'name'),
195
            ]
196
        );
197
    }
198
199
    /**
200
     * Displays a single Order model.
201
     * @param string $id
202
     * @return mixed
203
     */
204
    public function actionView($id)
205
    {
206
        $model = $this->findModel($id);
207
        $orderIsImmutable = $model->getImmutability(\app\modules\shop\models\Order::IMMUTABLE_MANAGER);
208
209
        $transactionsDataProvider = new ArrayDataProvider([
210
            'allModels' => $model->transactions,
211
        ]);
212
213
        if (Yii::$app->request->isPost && !$orderIsImmutable) {
214
            $model->setScenario('backend');
215
            if ($model->load(Yii::$app->request->post())) {
216
                /** @var OrderDeliveryInformation $orderDeliveryInformation */
217
                $orderDeliveryInformation = $model->orderDeliveryInformation;
218
                if ($orderDeliveryInformation->load(Yii::$app->request->post())) {
219
                    $orderDeliveryInformation->saveModelWithProperties(Yii::$app->request->post());
220
                }
221
222
                $model->save();
223
            }
224
        }
225
        $lastMessages = OrderChat::find()
226
            ->where(['order_id' => $id])
227
            ->orderBy(['id' => SORT_DESC])
228
            ->all();
229
        return $this->render(
230
            'view',
231
            [
232
                'lastMessages' => $lastMessages,
233
                'managers' => $this->getManagersList(),
234
                'model' => $model,
235
                'transactionsDataProvider' => $transactionsDataProvider,
236
                'orderIsImmutable' => $orderIsImmutable,
237
            ]
238
        );
239
    }
240
241
    /**
242
     * Update order status action.
243
     * @param integer|null $id
244
     * @return array
245
     * @throws \yii\web\BadRequestHttpException
246
     */
247
    public function actionUpdateStage($id = null)
248
    {
249
        Yii::$app->response->format = Response::FORMAT_JSON;
250
        $post = Yii::$app->request->post();
251
        if ($id === null) {
252
            if (!isset($post['editableIndex'], $post['editableKey'],
253
                $post['Order'][$post['editableIndex']]['order_stage_id'])) {
254
                throw new BadRequestHttpException;
255
            }
256
            $id = $post['editableKey'];
257
            $value = $post['Order'][$post['editableIndex']]['order_stage_id'];
258
        } else {
259
            if (!isset($post['Order']['order_stage_id'])) {
260
                throw new BadRequestHttpException;
261
            }
262
            $value = $post['Order']['order_stage_id'];
263
        }
264
        $order = $this->findModel($id);
265
        $order->order_stage_id = $value;
266
        /** @var OrderStage $orderStage */
267
        $orderStage = OrderStage::findOne($value);
268 View Code Duplication
        if ($orderStage === null || !$order->save(true, ['order_stage_id'])) {
269
            return [
270
                'message' => Yii::t('app', 'Cannot change order stage'),
271
            ];
272
        }
273
        return [
274
            'output' => Html::tag('span', $orderStage->name_short),
275
        ];
276
    }
277
278
    /**
279
     * @param $id
280
     * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,string>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
281
     * @throws BadRequestHttpException
282
     * @throws NotFoundHttpException
283
     */
284
    public function actionUpdateShippingOption($id)
285
    {
286
        Yii::$app->response->format = Response::FORMAT_JSON;
287
        $post = Yii::$app->request->post();
288
        if (!isset($post['OrderDeliveryInformation']['shipping_option_id'])) {
289
            throw new BadRequestHttpException;
290
        }
291
        /** @var OrderDeliveryInformation $orderDeliveryInformation */
292
        $orderDeliveryInformation = OrderDeliveryInformation::findOne($id);
293
        if (is_null($orderDeliveryInformation)) {
294
            throw new NotFoundHttpException;
295
        }
296
        $value = $post['OrderDeliveryInformation']['shipping_option_id'];
297
        $orderDeliveryInformation->shipping_option_id = $value;
298
        /** @var ShippingOption $shippingOption */
299
        $shippingOption = ShippingOption::findOne($value);
300
        // @todo Need to save shipping price
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
301
        if (is_null($shippingOption) || !$orderDeliveryInformation->save(true, ['shipping_option_id'])) {
302
            return [
303
                'message' => Yii::t('app', 'Cannot change shipping option'),
304
            ];
305
        }
306
        return [
307
            'output' => $shippingOption->name,
308
        ];
309
    }
310
311
    /**
312
     * @param integer $id
313
     * @return array
314
     * @throws \yii\web\BadRequestHttpException
315
     */
316
    public function actionChangeManager($id)
0 ignored issues
show
Coding Style introduced by
actionChangeManager uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
317
    {
318
        Yii::$app->response->format = Response::FORMAT_JSON;
319
        $order = $this->findModel($id);
320
        if (!isset($_POST['Order']['manager_id'])) {
321
            throw new BadRequestHttpException;
322
        }
323
        /** @var \app\modules\user\models\User|null $user */
324
        $user = User::findOne($_POST['Order']['manager_id']);
325
        if (is_null($user) || !Yii::$app->authManager->checkAccess($user->id, 'order manage')) {
326
            throw new BadRequestHttpException;
327
        }
328
        $order->scenario = 'changeManager';
329
        $oldManager = $order->manager;
330
        $order->load($_POST);
331
        if (!$order->save()) {
332
            return [
333
                'message' => Yii::t('app', 'Cannot change manager'),
334
            ];
335
        }
336
        if (is_null($oldManager) || $oldManager->id != $order->manager_id) {
337
            // send message
338
            try {
339
                $to = [$user->email];
340
                if (!is_null($oldManager)) {
341
                    $to[] = $oldManager->email;
342
                    $subject =  Yii::t(
343
                        'app',
344
                        'Manager has been changed from {oldManagerName} to {newManagerName}. Order #{orderId}',
345
                        [
346
                            'oldManagerName' => $oldManager->getDisplayName(),
347
                            'newManagerName' => $user->getDisplayName(),
348
                            'orderId' => $order->id,
349
                        ]
350
                    );
351
                    Notification::addNotification(
352
                        $oldManager->id,
353
                        Yii::t(
354
                            'app',
355
                            'You are not a manager of <a href="{orderUrl}" target="_blank">order #{orderId}</a> already',
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 121 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
356
                            [
357
                                'orderId' =>$order->id,
358
                                'orderUrl' => Url::toRoute(['/backend/order/view', 'id' => $order->id]),
359
                            ]
360
                        ),
361
                        'Order',
362
                        'info'
363
                    );
364
                } else {
365
                    $subject =  Yii::t(
366
                        'app',
367
                        'Manager has been changed to {newManagerName}. Order #{orderId}',
368
                        [
369
                            'newManagerName' => $user->getDisplayName(),
370
                            'orderId' => $order->id,
371
                        ]
372
                    );
373
                }
374
                Notification::addNotification(
375
                    $user->id,
376
                    Yii::t(
377
                        'app',
378
                        'You are a new manager of <a href="{orderUrl}" target="_blank">order #{orderId}</a>',
379
                        [
380
                            'orderId' =>$order->id,
381
                            'orderUrl' => Url::toRoute(['/backend/order/view', 'id' => $order->id]),
382
                        ]
383
                    ),
384
                    'Order',
385
                    'info'
386
                );
387
                Yii::$app->mail
388
                    ->compose(
389
                        '@app/backend/views/order/change-manager-email-template',
390
                        [
391
                            'manager' => $user,
392
                            'oldManager' => $oldManager,
393
                            'order' => $order,
394
                            'user' => Yii::$app->user->getIdentity(),
395
                        ]
396
                    )
397
                    ->setTo($to)
398
                    ->setFrom(Yii::$app->mail->transport->getUsername())
399
                    ->setSubject($subject)
400
                    ->send();
401
            } catch (\Exception $e) {
402
                // do nothing
403
            }
404
        }
405
        return [
406
            'output' => Html::encode($user->username),
407
        ];
408
    }
409
410 View Code Duplication
    public function actionDelete($id = null)
411
    {
412
        /** @var Order $model */
413
        if ((null === $id) || (null === $model = Order::findOne($id))) {
414
            throw new NotFoundHttpException;
415
        }
416
417
        if (!$model->delete()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $model->delete() of type false|integer is loosely compared to false; this is ambiguous if the integer can be zero. 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 integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
418
            Yii::$app->session->setFlash('info', Yii::t('app', 'The object is placed in the cart'));
419
        } else {
420
            Yii::$app->session->setFlash('info', Yii::t('app', 'Object removed'));
421
        }
422
423
        return $this->redirect(
424
            Yii::$app->request->get(
425
                'returnUrl',
426
                Url::toRoute(['index'])
427
            )
428
        );
429
    }
430
431
    public function actionDeleteOrderItem($id)
432
    {
433
        /** @var OrderItem $orderItem */
434
        $orderItem = OrderItem::findOne($id);
435
        if (is_null($orderItem)) {
436
            throw new NotFoundHttpException();
437
        }
438
        $orderItem->delete();
439
        $orderItem->order->calculate(true);
440
        return $this->redirect(['view', 'id' => $orderItem->order->id]);
441
    }
442
443
    public function actionChangeOrderItemQuantity($id)
444
    {
445
        Yii::$app->response->format = Response::FORMAT_JSON;
446
        /** @var OrderItem $orderItem */
447
        $orderItem = OrderItem::findOne($id);
448
        $orderItem->load(Yii::$app->request->post());
449
        $orderItem->quantity = $orderItem->product->measure->ceilQuantity($orderItem->quantity);
450
        $orderItem->price_per_pcs = PriceHelper::getProductPrice(
451
            $orderItem->product,
0 ignored issues
show
Bug introduced by
It seems like $orderItem->product can also be of type object<app\properties\HasProperties>; however, app\modules\shop\helpers...lper::getProductPrice() does only seem to accept object<app\modules\shop\models\Product>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
452
            $orderItem->order,
453
            1,
454
            SpecialPriceList::TYPE_CORE
455
        );
456
        if (!$orderItem->save(true, ['quantity', 'total_price', 'discount_amount', 'total_price_without_discount'])
457
            || !$orderItem->order->calculate(true)
458
        ) {
459
            return [
460
                'message' => Yii::t('app', 'Cannot change quantity'),
461
                'error' => $orderItem->errors,
462
            ];
463
        }
464
        return [
465
            'output' => $orderItem->quantity,
466
        ];
467
    }
468
469
    public function actionAutoCompleteSearch($orderId, $term, $parentId = 0)
470
    {
471
        Yii::$app->response->format = Response::FORMAT_JSON;
472
        $product = Yii::$container->get(Product::class);
473
        $query = $product::find()->orderBy('sort_order');
474
        foreach (['name', 'content'] as $attribute) {
475
            $query->orWhere(['like', $attribute, $term]);
476
        }
477
        $products = $query->limit(20)->all();
478
        $result = [];
479
        /** @var Product $productModel */
480
        foreach ($products as $productModel) {
481
            $result[] = [
482
                'name' => $productModel->name,
483
                'url' => Url::toRoute([
484
                    'add-product',
485
                    'orderId' => $orderId,
486
                    'productId' => $productModel->id,
487
                    'parentId' => $parentId,
488
                ]),
489
            ];
490
        }
491
        return $result;
492
    }
493
494
    public function actionAddProduct($orderId, $productId, $parentId = 0)
495
    {
496
        $order = $this->findModel($orderId);
497
        /** @var OrderItem $orderItem */
498
        $orderItem = OrderItem::findOne(['product_id' => $productId, 'order_id' => $orderId]);
499
        $product = Yii::$container->get(Product::class);
500
        /** @var Product $productModel */
501
        $productModel = $product::findById($productId);
502
        if (is_null($orderItem)) {
503
            $orderItem = new OrderItem;
504
            $orderItem->attributes = [
505
                'parent_id' => $parentId,
506
                'order_id' => $order->id,
507
                'product_id' => $productModel->id,
508
                'quantity' => $productModel->measure->nominal,
509
                'price_per_pcs' =>  PriceHelper::getProductPrice(
510
                    $productModel,
511
                    $order,
512
                    1,
513
                    SpecialPriceList::TYPE_CORE
514
                ),
515
            ];
516
        } else {
517
            $orderItem->quantity++;
518
        }
519
        $orderItem->save();
520
        $order->calculate(true);
521
        return $this->redirect(['view', 'id' => $orderId]);
522
    }
523
524
    public function actionUpdateOrderProperties($id)
525
    {
526
        /** @var Order|HasProperties $model */
527
        $model = $this->findModel($id);
528
        $model->abstractModel->setAttributesValues(Yii::$app->request->post());
529
        if ($model->abstractModel->validate()) {
530
            $model->getPropertyGroups(true);
0 ignored issues
show
Bug introduced by
The method getPropertyGroups does only exist in app\properties\HasProperties, but not in app\modules\shop\models\Order.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
531
            $model->saveProperties(Yii::$app->request->post());
0 ignored issues
show
Bug introduced by
The method saveProperties does only exist in app\properties\HasProperties, but not in app\modules\shop\models\Order.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
532
        }
533
        return $this->redirect(['view', 'id' => $id]);
534
    }
535
536
    /**
537
     * @return string|Response
538
     * @throws \yii\base\Exception
539
     * @throws \yii\web\ServerErrorHttpException
540
     */
541
    public function actionCreate()
542
    {
543
        $model = Order::create(false, false, true);
544
        $model->setScenario('backend');
545
546
        if (Yii::$app->request->isPost) {
547
            $data = Yii::$app->request->post();
548
            if ($model->load($data) && $model->validate()) {
549
                if (User::USER_GUEST === intval($model->user_id)) {
550
                    $model->customer_id = 0;
551
                } elseif (null === Customer::findOne(['user_id' => intval($model->user_id), 'id' => intval($model->customer_id)])) {
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 132 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
552
                    $model->customer_id = 0;
553
                }
554
555
                if (0 === intval($model->customer_id)) {
556
                    $customer = Customer::createEmptyCustomer(intval($model->user_id));
557 View Code Duplication
                    if ($customer->load($data) && $customer->save()) {
558
                        if (!empty($customer->getPropertyGroup())) {
559
                            $customer->getPropertyGroup()->appendToObjectModel($customer);
560
                            $data[$customer->getAbstractModel()->formName()] = isset($data['CustomerNew']) ? $data['CustomerNew'] : [];
0 ignored issues
show
Documentation Bug introduced by
The method getAbstractModel does not exist on object<app\modules\shop\models\Customer>? 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...
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 135 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
561
                        }
562
                        $customer->saveModelWithProperties($data);
563
                        $customer->refresh();
564
                        $model->customer_id = $customer->id;
565
                    }
566
                } else {
567
                    $customer = Customer::findOne(['id' => $model->customer_id]);
568
                }
569
570
                if (0 === $model->contragent_id || null === Contragent::findOne(['id' => $model->contragent_id, 'customer_id' => $model->customer_id])) {
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 153 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
571
                    $contragent = Contragent::createEmptyContragent($customer);
0 ignored issues
show
Bug introduced by
It seems like $customer can be null; however, createEmptyContragent() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
572 View Code Duplication
                    if ($contragent->load($data) && $contragent->save()) {
0 ignored issues
show
Bug introduced by
The method load does only exist in app\modules\shop\models\Contragent, but not in app\properties\HasProperties.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
Bug introduced by
The method save does only exist in app\modules\shop\models\Contragent, but not in app\properties\HasProperties.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
573
                        if (!empty($contragent->getPropertyGroup())) {
574
                            $contragent->getPropertyGroup()->appendToObjectModel($contragent);
0 ignored issues
show
Bug introduced by
The method getPropertyGroup does only exist in app\modules\shop\models\Contragent, but not in app\properties\HasProperties.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
575
                            $data[$contragent->getAbstractModel()->formName()] = isset($data['ContragentNew']) ? $data['ContragentNew'] : [];
0 ignored issues
show
Bug introduced by
The method getAbstractModel does only exist in app\properties\HasProperties, but not in app\modules\shop\models\Contragent.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 141 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
576
                        }
577
                        $contragent->saveModelWithProperties($data);
0 ignored issues
show
Bug introduced by
The method saveModelWithProperties does only exist in app\modules\shop\models\Contragent, but not in app\properties\HasProperties.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
578
                        $contragent->refresh();
0 ignored issues
show
Bug introduced by
The method refresh does only exist in app\modules\shop\models\Contragent, but not in app\properties\HasProperties.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
579
                        $model->contragent_id = $contragent->id;
580
                    }
581
                } else {
582
                    $contragent = Contragent::findOne(['id' => $model->contragent_id]);
583
                }
584
585
                if ($model->save()) {
586
                    OrderDeliveryInformation::createNewOrderDeliveryInformation($model, false);
587
                    DeliveryInformation::createNewDeliveryInformation($contragent, false);
0 ignored issues
show
Bug introduced by
It seems like $contragent can also be of type null or object<app\properties\HasProperties>; however, app\modules\shop\models\...ewDeliveryInformation() does only seem to accept object<app\modules\shop\models\Contragent>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
588
                    return $this->redirect(Url::toRoute([
589
                        'view', 'id' => $model->id
590
                    ]));
591
                }
592
            }
593
        }
594
595
        return $this->render('create', [
596
            'model' => $model,
597
        ]);
598
    }
599
600
    /**
601
     * @param $id
602
     * @return array
603
     * @throws BadRequestHttpException
604
     * @throws NotFoundHttpException
605
     */
606
    public function actionUpdatePaymentType($id)
607
    {
608
        Yii::$app->response->format = Response::FORMAT_JSON;
609
        $post = Yii::$app->request->post();
610
        if (!isset($post['Order']['payment_type_id'])) {
611
            throw new BadRequestHttpException;
612
        }
613
        $value = $post['Order']['payment_type_id'];
614
        $order = $this->findModel($id);
615
        $order->payment_type_id = $value;
616
        /** @var PaymentType $paymentType */
617
        $paymentType = PaymentType::findOne($value);
618 View Code Duplication
        if ($paymentType === null || !$order->save(true, ['payment_type_id'])) {
619
            return [
620
                'message' => Yii::t('app', 'Cannot change a payment type'),
621
            ];
622
        }
623
        return [
624
            'output' => Html::tag('span', $paymentType->name),
625
        ];
626
    }
627
628
    /**
629
     * Add new message to OrderChat
630
     * @param $orderId
631
     * @return int[]
0 ignored issues
show
Documentation introduced by
Should the return type not be array<string,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...
632
     * @throws BadRequestHttpException
633
     */
634
    public function actionSendToOrderChat($orderId)
635
    {
636
        Yii::$app->response->format = Response::FORMAT_JSON;
637
638
        /** @var Order $order */
639
        $order = Order::findOne($orderId);
640
        if (null === $order) {
641
            throw new BadRequestHttpException();
642
        }
643
644
        $message = new OrderChat();
645
        $message->loadDefaultValues();
646
        $message->message = Yii::$app->request->post('message');
647
        $message->order_id = $order->id;
648
        $message->user_id = Yii::$app->user->id;
649
        if ($message->save()) {
650
            if ($order->manager_id != Yii::$app->user->id) {
651
                Notification::addNotification(
652
                    $order->manager_id,
653
                    Yii::t(
654
                        'app',
655
                        'Added a new comment to <a href="{orderUrl}" target="_blank">order #{orderId}</a>',
656
                        [
657
                            'orderUrl' => Url::toRoute(['/backend/order/view', 'id' => $order->id]),
658
                            'orderId' => $order->id,
659
                        ]
660
                    ),
661
                    'Order',
662
                    'info'
663
                );
664
            }
665
            $message->refresh();
666
            $user = $message->user;
667
            return [
668
                'status' => 1,
669
                'message' => $message->message,
670
                'user' => null !== $user ? $user->username : Yii::t('app', 'Unknown'),
671
                'gravatar' => null !== $user ? $user->gravatar() : null,
672
                'date' => $message->date,
673
            ];
674
        }
675
676
        return ['status' => 0];
677
    }
678
679
    /**
680
     * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,false|array>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
681
     */
682
    public function actionAjaxUser()
683
    {
684
        \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
685
686
        $result = [
687
            'more' => false,
688
            'results' => []
689
        ];
690
        $search = \Yii::$app->request->get('search', []);
691
        if (!empty($search['term'])) {
692
            $query = User::find()
693
                ->select('id, username, first_name, last_name, email')
694
                ->where(['like', 'username', trim($search['term'])])
695
                ->orWhere(['like', 'email', trim($search['term'])])
696
                ->orWhere(['like', 'first_name', trim($search['term'])])
697
                ->orWhere(['like', 'last_name', trim($search['term'])])
698
                ->asArray();
699
700
            $result['results'] = array_values($query->all());
701
        }
702
703
        return $result;
704
    }
705
706
    /**
707
     * @return array
708
     */
709
    public function actionAjaxCustomer($template = null)
710
    {
711
        \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
712
713
        $result = [
714
            'more' => false,
715
            'results' => []
716
        ];
717
        $search = \Yii::$app->request->get('search', []);
718
        $user_id = isset($search['user']) && User::USER_GUEST !== intval($search['user']) ? intval($search['user']) : User::USER_GUEST;
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 135 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
719
        if (!empty($search['term'])) {
720
            $query = Customer::find()
721
                ->select('id, first_name, middle_name, last_name, email, phone')
722
                ->where(['user_id' => $user_id])
723
                ->andWhere('first_name LIKE :term1 OR middle_name LIKE :term2 OR last_name LIKE :term3 OR email LIKE :term4 OR phone LIKE :term5', [
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 148 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
724
                    ':term1' => '%'.trim($search['term']).'%',
725
                    ':term2' => '%'.trim($search['term']).'%',
726
                    ':term3' => '%'.trim($search['term']).'%',
727
                    ':term4' => '%'.trim($search['term']).'%',
728
                    ':term5' => '%'.trim($search['term']).'%',
729
                ])
730
                ->asArray();
731
732
            $result['results'] = array_values($query->all());
733
        }
734
735 View Code Duplication
        if (!empty($result['results']) && 'simple' === $template) {
736
            $result['cards'] = array_reduce($result['results'],
737
                function ($result, $item)
738
                {
739
                    /** @var array $item */
740
                    $result[$item['id']] = \app\modules\shop\widgets\Customer::widget([
741
                        'viewFile' => 'customer/backend_list',
742
                        'model' => Customer::findOne(['id' => $item['id']]),
743
                    ]);
744
                    return $result;
745
                }, []);
746
        }
747
748
        return $result;
749
    }
750
751
    /**
752
     * @return array
753
     */
754
    public function actionAjaxContragent($template = null)
755
    {
756
        \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
757
        $result = [
758
            'more' => false,
759
            'results' => [[0 => Yii::t('app', 'New contragent')]]
760
        ];
761
        $search = \Yii::$app->request->get('search', []);
762
        $customer_id = isset($search['customer']) ? intval($search['customer']) : 0;
763
        $query = Contragent::find()
764
            ->select('id, type')
765
            ->where(['customer_id' => $customer_id])
766
            ->asArray();
767
        $result['results'] = array_merge(array_values($query->all()), $result['results']);
768
769 View Code Duplication
        if (!empty($result['results']) && 'simple' === $template) {
770
            $result['cards'] = array_reduce($result['results'],
771
                function ($result, $item)
772
                {
773
                    /** @var array $item */
774
                    if (!empty($item['id'])) {
775
                        $result[$item['id']] = \app\modules\shop\widgets\Contragent::widget([
776
                            'viewFile' => 'contragent/backend_list',
777
                            'model' => Contragent::findOne(['id' => $item['id']]),
778
                        ]);
779
                    }
780
                    return $result;
781
                }, []);
782
        }
783
784
        return $result;
785
    }
786
}
787