Completed
Push — master ( 624813...e6957a )
by Dmitry
07:06
created

PlanController::actionSuggestCommonPricesModal()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 0
cts 9
cp 0
rs 9.8666
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 2
1
<?php
2
3
namespace hipanel\modules\finance\controllers;
4
5
use hipanel\actions\Action;
6
use hipanel\actions\IndexAction;
7
use hipanel\actions\SmartCreateAction;
8
use hipanel\actions\SmartDeleteAction;
9
use hipanel\actions\SmartPerformAction;
10
use hipanel\actions\SmartUpdateAction;
11
use hipanel\actions\ValidateFormAction;
12
use hipanel\actions\ViewAction;
13
use hipanel\base\CrudController;
14
use hipanel\helpers\ArrayHelper;
15
use hipanel\modules\finance\collections\PricesCollection;
16
use hipanel\modules\finance\helpers\PlanInternalsGrouper;
17
use hipanel\modules\finance\helpers\PriceChargesEstimator;
18
use hipanel\modules\finance\helpers\PriceSort;
19
use hipanel\modules\finance\models\factories\PriceModelFactory;
20
use hipanel\modules\finance\models\Plan;
21
use hipanel\modules\finance\models\PriceSuggestionRequestForm;
22
use hipanel\modules\server\models\Server;
23
use hipanel\filters\EasyAccessControl;
24
use hipanel\modules\finance\models\Price;
25
use hipanel\modules\finance\models\TargetObject;
26
use hiqdev\hiart\ResponseErrorException;
27
use Yii;
28
use yii\base\Event;
29
use yii\base\Module;
30
use yii\web\NotFoundHttpException;
31
use yii\web\Response;
32
use yii\web\UnprocessableEntityHttpException;
33
34
class PlanController extends CrudController
35
{
36
    /**
37
     * @var PriceModelFactory
38
     */
39
    public $priceModelFactory;
40
41
    /**
42
     * PlanController constructor.
43
     * @param string $id
44
     * @param Module $module
45
     * @param PriceModelFactory $priceModelFactory
46
     * @param array $config
47
     */
48
    public function __construct(string $id, Module $module, PriceModelFactory $priceModelFactory, array $config = [])
49
    {
50
        parent::__construct($id, $module, $config);
51
52
        $this->priceModelFactory = $priceModelFactory;
53
    }
54
55
    public function behaviors()
56
    {
57
        return array_merge(parent::behaviors(), [
58
            [
59
                'class' => EasyAccessControl::class,
60
                'actions' => [
61
                    'create' => 'plan.create',
62
                    'update' => 'plan.update',
63
                    'update-prices' => 'plan.update',
64
                    'templates' => 'plan.create',
65
                    'create-prices' => 'plan.create',
66
                    'delete' => 'plan.delete',
67
                    '*' => 'plan.read',
68
                ],
69
            ],
70
        ]);
71
    }
72
73
    public function actions()
74
    {
75
        return array_merge(parent::actions(), [
76
            'create' => [
77
                'class' => SmartCreateAction::class,
78
                'success' => Yii::t('hipanel.finance.plan', 'Plan was successfully created'),
79
            ],
80
            'update' => [
81
                'class' => SmartUpdateAction::class,
82
                'success' => Yii::t('hipanel.finance.plan', 'Plan was successfully updated'),
83
            ],
84
            'index' => [
85
                'class' => IndexAction::class,
86
            ],
87
            'view' => [
88
                'class' => ViewAction::class,
89
                'on beforePerform' => function (Event $event) {
90
                    $action = $event->sender;
91
                    $action->getDataProvider()->query
92
                        ->joinWith('sales')
93
                        ->andWhere(['state' => ['ok', 'deleted']])
94
                        ->withPrices();
95
                },
96
                'data' => function (Action $action, array $data) {
97
                    return array_merge($data, [
98
                        'grouper' => new PlanInternalsGrouper($data['model']),
99
                        'parentPrices' => $this->getParentPrices($data['model']['id'])
100
                    ]);
101
                },
102
            ],
103
            'set-note' => [
104
                'class' => SmartUpdateAction::class,
105
                'success' => Yii::t('hipanel', 'Note changed'),
106
            ],
107
            'validate-form' => [
108
                'class' => ValidateFormAction::class,
109
            ],
110
            'validate-single-form' => [
111
                'class' => ValidateFormAction::class,
112
                'validatedInputId' => false,
113
            ],
114
            'delete' => [
115
                'class' => SmartDeleteAction::class,
116
                'success' => Yii::t('hipanel.finance.plan', 'Plan was successfully deleted'),
117
            ],
118
            'restore' => [
119
                'class' => SmartPerformAction::class,
120
                'success' => Yii::t('hipanel.finance.plan', 'Plan was successfully restored'),
121
            ],
122
            'copy' => [
123
                'class' => SmartUpdateAction::class,
124
                'view' => 'modals/copy',
125
                'queryOptions' => ['batch' => false],
126
            ],
127
        ]);
128
    }
129
130
    public function actionCreatePrices(int $plan_id, int $template_plan_id)
131
    {
132
        $plan = $this->findPlan($template_plan_id);
133
134
        $suggestions = (new Price)->batchQuery('suggest', [
135
            'object_id' => $plan_id,
136
            'plan_id' => $plan_id,
137
            'template_plan_id' => $template_plan_id,
138
            'type' => $plan->type,
139
        ]);
140
141
        $this->populateWithPrices($plan, $suggestions);
0 ignored issues
show
Documentation introduced by
$plan is of type object<yii\db\ActiveRecordInterface>|array, but the function expects a object<hipanel\modules\finance\models\Plan>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
142
        $parentPrices = $this->getParentPrices($plan_id);
143
144
        $targetPlan = Plan::findOne(['id' => $plan_id]);
145
        $grouper = new PlanInternalsGrouper($plan);
0 ignored issues
show
Documentation introduced by
$plan is of type object<yii\db\ActiveRecordInterface>|array, but the function expects a object<hipanel\modules\finance\models\Plan>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
146
        [$plan->name, $plan->id] = [$targetPlan->name, $targetPlan->id];
147
        $action = ['@plan/update-prices', 'id' => $plan->id, 'scenario' => 'create'];
148
149
        return $this->render($plan->type . '/' . 'createPrices',
150
            compact('plan', 'grouper', 'parentPrices', 'action', 'plan_id'));
151
    }
152
153
    public function actionSuggestPricesModal($id)
154
    {
155
        /** @var Plan $plan */
156
        $plan = $this->findPlan($id);
157
        $model = new PriceSuggestionRequestForm([
158
            'plan_id' => $plan->id,
159
            'plan_type' => $plan->type,
160
        ]);
161
162
        return $this->renderAjax('modals/suggestPrices', compact('plan', 'model'));
163
    }
164
165
    public function actionSuggestGroupingPricesModal($id)
166
    {
167
        /** @var Plan $plan */
168
        $plan = $this->findPlan($id);
169
        $model = new PriceSuggestionRequestForm([
170
            'plan_id' => $plan->id,
171
            'plan_type' => $plan->type,
172
            'object_id' => $plan->id,
173
            'scenario' => PriceSuggestionRequestForm::SCENARIO_PREDEFINED_OBJECT,
174
        ]);
175
176
        return $this->renderAjax('modals/suggestPrices', compact('plan', 'model'));
177
    }
178
179
    public function actionSuggestSharedPricesModal($id)
180
    {
181
        /** @var Plan $plan */
182
        $plan = $this->findPlan($id);
183
        $model = new PriceSuggestionRequestForm([
184
            'plan_id' => $plan->id,
185
            'plan_type' => $plan->type,
186
            'scenario' => PriceSuggestionRequestForm::SCENARIO_PREDEFINED_OBJECT,
187
        ]);
188
189
        return $this->renderAjax('modals/suggestPrices', compact('plan', 'model'));
190
    }
191
192
    /**
193
     * @param $id integer
194
     * @return Plan|null
195
     * @throws NotFoundHttpException
196
     */
197
    private function findPlan(int $id): ?Plan
198
    {
199
        $plan = Plan::findOne(['id' => $id]);
200
        if ($plan === null) {
201
            throw new NotFoundHttpException('Not found');
202
        }
203
204
        return $plan;
205
    }
206
207
    /**
208
     * @param string $plan_id
209
     * @param string|null $object_id Object ID or `null`
210
     * when the desired templates are not related to a specific object
211
     * @param string $name_ilike
212
     * @return array
213
     */
214
    public function actionTemplates($plan_id, $object_id = null, string $name_ilike = null)
215
    {
216
        $templates = (new Plan())->query('search-templates', [
217
            'id' => $plan_id,
218
            'object_id' => $object_id ?? $plan_id,
219
            'name_ilike' => $name_ilike
220
        ]);
221
222
        Yii::$app->response->format = Response::FORMAT_JSON;
223
224
        return $templates;
225
    }
226
227
    public function actionCalculateCharges()
228
    {
229
        Yii::$app->response->format = Response::FORMAT_JSON;
230
        $request = Yii::$app->request;
231
232
        $periods = ['now', 'first day of +1 month', 'first day of +1 year'];
233
        $calculations = Plan::perform('calculate-charges', [
234
            'actions' => $request->post('actions'),
235
            'prices' => $request->post('prices'),
236
            'times' => $periods,
237
        ]);
238
        /** @var PriceChargesEstimator $calculator */
239
        $calculator = Yii::$container->get(PriceChargesEstimator::class, [$calculations]);
240
241
        try {
242
            return $calculator->calculateForPeriods($periods);
243
        } catch (ResponseErrorException $exception) {
244
            Yii::$app->response->setStatusCode(412, $exception->getMessage());
245
            return [
246
                'formula' => $exception->getResponse()->getData()['_error_ops']['formula'] ?? null
247
            ];
248
        }
249
    }
250
251
    public function actionCalculateValues($planId)
252
    {
253
        Yii::$app->response->format = Response::FORMAT_JSON;
254
        $periods = ['now', 'first day of +1 month', 'first day of +1 year'];
255
        try {
256
            $calculations = Plan::perform('calculate-values', ['id' => $planId, 'times' => $periods]);
257
            $calculator = Yii::$container->get(PriceChargesEstimator::class, [$calculations]);
258
259
            return $calculator->calculateForPeriods($periods);
260
        } catch (ResponseErrorException $exception) {
261
            Yii::$app->response->setStatusCode(412, $exception->getMessage());
262
263
            return [
264
                'formula' => $exception->getResponse()->getData()['_error_ops']['formula'] ?? null
265
            ];
266
        }
267
268
    }
269
270
    public function actionUpdatePrices(int $id, string $scenario = 'update')
271
    {
272
        $plan = Plan::find()
273
            ->byId($id)
274
            ->withPrices()
275
            ->one();
276
277
        $request = Yii::$app->request;
278
        if ($request->isPost) {
279
            try {
280
                $collection = new PricesCollection($this->priceModelFactory, ['scenario' => $scenario]);
281
                $collection->load();
282
                if ($collection->save() === false) {
283
                    if ($scenario === 'create') {
284
                        Yii::$app->session->addFlash('error', Yii::t('hipanel.finance.price', 'Error occurred during creation of prices'));
285
                    } elseif ($scenario === 'update') {
286
                        Yii::$app->session->addFlash('error', Yii::t('hipanel.finance.price', 'Error occurred during prices update'));
287
                    }
288
                } else {
289
                    if ($scenario === 'create') {
290
                        Yii::$app->session->addFlash('success', Yii::t('hipanel.finance.price', 'Prices were successfully created'));
291
                    } elseif ($scenario === 'update') {
292
                        Yii::$app->session->addFlash('success', Yii::t('hipanel.finance.price', 'Prices were successfully updated'));
293
                    }
294
                }
295
                return $this->redirect(['@plan/view', 'id' => $id]);
296
            } catch (\Exception $e) {
297
                throw new UnprocessableEntityHttpException($e->getMessage(), 0, $e);
298
            }
299
        }
300
301
        $grouper = new PlanInternalsGrouper($plan);
0 ignored issues
show
Documentation introduced by
$plan is of type object<hiqdev\hiart\ActiveRecord>|array|null, but the function expects a object<hipanel\modules\finance\models\Plan>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
302
        $parentPrices = $this->getParentPrices($id);
303
304
        return $this->render($plan->type . '/' . 'updatePrices',
305
            compact('plan', 'grouper', 'parentPrices'));
306
    }
307
308
    /**
309
     * @param int $plan_id
310
     * @return array | null
311
     */
312
    private function getParentPrices(int $plan_id)
313
    {
314
        $parent_id = (new Plan())->query('get-parent-id', [
315
            'id' => $plan_id,
316
        ]);
317
        $parent_id = $parent_id['parent_id'];
318
        if ($parent_id === null) {
319
            return null;
320
        }
321
322
        $parent = Plan::find()
323
            ->byId($parent_id)
324
            ->withPrices()
325
            ->one();
326
327
        return $parent ? (new PlanInternalsGrouper($parent))->group() : null;
0 ignored issues
show
Documentation introduced by
$parent is of type object<hiqdev\hiart\ActiveRecord>|array, but the function expects a object<hipanel\modules\finance\models\Plan>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
328
    }
329
330
    /**
331
     * @param Plan $plan
332
     * @param array $pricesData
333
     */
334
    private function populateWithPrices(Plan $plan, $pricesData): void
335
    {
336
        $prices = [];
337
        foreach ($pricesData as $priceData) {
338
            $object = ArrayHelper::remove($priceData, 'object');
339
            if (isset($priceData['plan_type']) &&
340
                $priceData['plan_type'] === 'certificate') {
341
                $priceData['class'] = 'CertificatePrice';
342
            }
343
344
            /** @var Price $price */
345
            $price = Price::instantiate($priceData);
346
            $price->setScenario('create');
347
            $price->setAttributes($priceData);
348
            $price->populateRelation('object', new TargetObject($object));
349
            $price->trigger(Price::EVENT_AFTER_FIND);
350
            $prices[] = $price;
351
        }
352
        $prices = PriceSort::anyPrices()->values($prices, true);
353
354
        $plan->populateRelation('prices', $prices);
355
    }
356
}
357