]> gitweb.erp-flowers.ru Git - erp24_rep/yii-erp24/.git/commitdiff
Расчет цели выбор магазина
authorfomichev <vladimir.fomichev@erp-flowers.ru>
Thu, 15 May 2025 13:52:57 +0000 (16:52 +0300)
committerfomichev <vladimir.fomichev@erp-flowers.ru>
Thu, 15 May 2025 13:52:57 +0000 (16:52 +0300)
erp24/controllers/BouquetController.php
erp24/services/StorePlanService.php
erp24/views/bouquet/month-goal-forecast.php [new file with mode: 0644]

index 6051e805b2ef1b4bec5ab876aa13c2c24a4d78f2..a31e736f491cf62d0552d7e5be50155ab06795a1 100644 (file)
@@ -301,7 +301,47 @@ class BouquetController extends Controller
             'year'      => $year,
         ]);
     }
+    public function actionMonthGoalForecast()
+    {
+        $request = Yii::$app->request;
+        $post = $request->post();
+        $month = $post['DynamicModel']['month'] ?? 5;
+        $year = $post['DynamicModel']['year'] ?? 2025;
+
+        $model = DynamicModel::validateData(
+            ['month' => $month, 'year' => $year],
+            [
+                [['month', 'year'], 'required'],
+                ['month', 'integer', 'min' => 1, 'max' => 12],
+                ['year', 'integer', 'min' => 2000, 'max' => 2100],
+            ]
+        );
+
+        if ($request->isPost && $model->validate()) {
+            $month = $model->month;
+            $year  = $model->year;
+        } else {
+            $model->month = $month;
+            $model->year  = $year;
+        }
+
+               $result = StorePlanService::getBouquetSpiecesMonthGoalFromForecast($month, $year, 2);
+//var_dump($result); die();
+        $stores = CityStore::find()
+            ->select(['id', 'name'])
+            ->where(['visible' => CityStore::IS_VISIBLE])
+            ->asArray()
+            ->all();
+        $storesMap = ArrayHelper::map($stores, 'id', 'name');
 
+        return $this->render('month-goal-forecast', [
+            'model'     => $model,   // передаём динамическую модель
+            'result'    => $result,
+            'storesMap' => $storesMap,
+            'month'     => $month,
+            'year'      => $year,
+        ]);
+    }
     public function actionGetGuidBouquets()
     {
         $model = new DynamicModel(['excelFile']);
index 97b8ddccd5dfca91339ee01ceee8a860b948eb10..650f13d1af8adcad228cd94d6e6bb1c58c8444cd 100755 (executable)
@@ -15,6 +15,7 @@ use yii_app\records\CityStoreParams;
 use yii_app\records\MatrixBouquetForecast;
 use yii_app\records\Motivation;
 use yii_app\records\PricesDynamic;
+use yii_app\records\Products1c;
 use yii_app\records\Products1cAdditionalCharacteristics;
 use yii_app\records\Products1cNomenclature;
 use yii_app\records\Sales;
@@ -910,22 +911,27 @@ class StorePlanService
     }
 
 
-    public static function getBouquetSpiecesMonthGoalFromForecast($month, $year)
+    public static function getBouquetSpiecesMonthGoalFromForecast($month, $year, $storeId = null)
     {
-        $stores = ArrayHelper::map(
-            CityStore::find()
-                ->select(['id'])
-                ->where(['visible' => CityStore::IS_VISIBLE])
-                ->asArray()
-                ->all(),
-            'id',
-            'id'
-        );
+        $stores = [];
 
+        if (!empty($storeId)) {
+            $stores[] = $storeId;
+        } else {
+            $stores = ArrayHelper::getColumn(
+                CityStore::find()
+                    ->select(['id'])
+                    ->where(['visible' => CityStore::IS_VISIBLE])
+                    ->asArray()
+                    ->all(),
+                'id'
+            );
+        }
+       // var_dump($stores); die();
         $storesParams = ArrayHelper::map(
             CityStoreParams::find()
                 ->select(['store_id', 'address_region'])
-                ->where(['store_id' => array_keys($stores)])
+                ->where(['store_id' => $stores])
                 ->andWhere(['not', ['address_region' => '']])
                 ->asArray()
                 ->indexBy('store_id')
@@ -934,13 +940,12 @@ class StorePlanService
             'address_region'
         );
 
-        // Все прогнозы на месяц
         $forecasts = MatrixBouquetForecast::find()
             ->where(['year' => $year, 'month' => $month])
             ->andWhere(['not', ['bouquet_id' => null]])
             ->asArray()
             ->all();
-
+       // var_dump($forecasts); die();
         $resultData = [];
         $debugData = [];
 
@@ -973,7 +978,7 @@ class StorePlanService
                 'l_store' => 'offline',
                 'xl_store' => 'offline',
                 'marketplace' => 'marketplace',
-                'internet' => 'internet'
+                'internet' => 'online'
             ];
 
             foreach ($types as $field => $typeSales) {
diff --git a/erp24/views/bouquet/month-goal-forecast.php b/erp24/views/bouquet/month-goal-forecast.php
new file mode 100644 (file)
index 0000000..c9b91dc
--- /dev/null
@@ -0,0 +1,291 @@
+<?php
+
+use kartik\grid\GridView;
+use yii\helpers\Html;
+use yii\helpers\Url;
+use yii\widgets\ActiveForm;
+use kartik\export\ExportMenu;
+use yii\data\ArrayDataProvider;
+
+/* @var $model yii\base\DynamicModel */
+/* @var $result array ['final', 'detail', 'debug'] */
+/* @var $storesMap array */
+/* @var $month int */
+/* @var $year int */
+
+$saleTypesLabels = [
+    1 => 'Оффлайн',
+    2 => 'Интернет магазины',
+    3 => 'Маркетплейсы',
+];
+
+?>
+<div class="show-goal p-4">
+    <h1>Цели букета на месяц: <?= Html::encode($month) ?>/<?= Html::encode($year) ?></h1>
+
+    <div class="month-goal-form col-4">
+
+        <?php $form = ActiveForm::begin([
+            'action' => Url::to(['bouquet/month-goal']),
+            'method' => 'post',
+        ]); ?>
+
+        <?= $form->field($model, 'month')->input('number')->label('Месяц') ?>
+        <?= $form->field($model, 'year')->input('number')->label('Год') ?>
+
+        <div class="form-group">
+            <?= Html::submitButton('Показать', ['class' => 'btn btn-primary']) ?>
+        </div>
+
+        <?php ActiveForm::end(); ?>
+    </div>
+
+    <hr>
+
+    <h2>Финальные суммы по магазинам</h2>
+<?php if (!empty($result['final'])): ?>
+    <?php foreach ($result['final'] as $storeId => $speciesData): ?>
+        <h3>Магазин: <?= Html::encode($storesMap[$storeId] ?? $storeId) ?></h3>
+        <?= GridView::widget([
+            'dataProvider' => new ArrayDataProvider([
+                'allModels' => array_map(fn($species, $sum) => [
+                    'species' => $species,
+                    'sum'     => $sum,
+                ], array_keys($speciesData), $speciesData),
+                'pagination' => false,
+            ]),
+            'columns' => [
+                ['attribute' => 'species', 'label' => 'Вид продукции'],
+                ['attribute' => 'sum',     'label' => 'Сумма'],
+            ],
+        ]) ?>
+    <?php endforeach; ?>
+<?php else: ?>
+    <p>Нет данных.</p>
+<?php endif; ?>
+
+    <hr>
+
+    <h2>Детальный расчёт по магазинам</h2>
+<?php if (!empty($result['detail'])): ?>
+    <?php foreach ($result['detail'] as $storeId => $speciesData): ?>
+        <h3>Магазин: <?= Html::encode($storesMap[$storeId] ?? $storeId) ?></h3>
+        <?= GridView::widget([
+            'dataProvider' => new ArrayDataProvider([
+                'allModels' => array_map(function ($species, $salesTypes) use ($saleTypesLabels) {
+                    $sum = array_sum($salesTypes);
+                    return [
+                        'species'     => $species,
+                        'offline'     => $salesTypes[1] ?? 0,
+                        'online'      => $salesTypes[2] ?? 0,
+                        'marketplace' => $salesTypes[3] ?? 0,
+                        'sum'         => $sum,
+                    ];
+                }, array_keys($speciesData), $speciesData),
+                'pagination' => false,
+            ]),
+            'columns' => [
+                ['attribute' => 'species',     'label' => 'Вид продукции'],
+                ['attribute' => 'offline',     'label' => 'Оффлайн'],
+                ['attribute' => 'online',      'label' => 'Интернет'],
+                ['attribute' => 'marketplace', 'label' => 'Маркетплейсы'],
+                ['attribute' => 'sum',         'label' => 'Итого'],
+            ],
+        ]) ?>
+    <?php endforeach; ?>
+<?php else: ?>
+    <p>Нет данных.</p>
+<?php endif; ?>
+
+    <hr>
+
+    <h2>Детальный разбор по магазинам</h2>
+<?php if (!empty($result['debug'])): ?>
+    <?php foreach ($result['debug'] as $storeId => $speciesData): ?>
+        <h3>Магазин: <?= Html::encode($storesMap[$storeId] ?? $storeId) ?></h3>
+        <?php foreach ($speciesData as $species => $saleTypes): ?>
+            <h4>Вид продукции: <?= Html::encode($species) ?></h4>
+            <?php foreach ($saleTypes as $saleType => $entries): ?>
+                <h5>Тип продаж: <?= Html::encode($saleTypesLabels[$saleType] ?? $saleType) ?></h5>
+                <?= GridView::widget([
+                    'dataProvider' => new ArrayDataProvider([
+                        'allModels' => $entries,
+                        'pagination' => false,
+                    ]),
+                    'columns' => [
+                        ['attribute' => 'product_guid',
+                            'value' => function ($model, $key, $index, $widget) {
+                                return \yii_app\records\Products1c::find()->where(['id' => $model['product_guid']])->one()->name .
+                                     " (" . $model['product_guid'] . ")";
+                            },
+                            'label' => 'Product GUID'],
+                        ['attribute' => 'bouquet_id',
+                            'value' => function ($model, $key, $index, $widget) {
+                                return \yii_app\records\MatrixBouquetForecast::find()->where(['bouquet_id' => $model['bouquet_id']])->one()->bouquet_name;
+                            },
+                            'label' => 'Букет'],
+                        ['attribute' => 'price',            'label' => 'Цена'],
+                        ['attribute' => 'count',            'label' => 'Кол-во'],
+                        ['attribute' => 'forecast',         'label' => 'Прогноз'],
+                        ['attribute' => 'raw_calculation',  'label' => 'Базовый расчёт'],
+                        ['attribute' => 'rounded',          'label' => 'С наценкой за сборку'],
+                    ],
+//                    'export' => [
+//                        'fontAwesome' => true
+//                    ],
+//                    'exportConfig' => [
+//                        'html' => [],
+//                        'csv' => [
+//                                'label' => Yii::t('kvgrid', 'CSV'),
+//                            'icon' => '',
+//                            'iconOptions' => ['class' => 'text-primary'],
+//                            'showHeader' => true,
+//                            'showPageSummary' => true,
+//                            'showFooter' => true,
+//                            'showCaption' => true,
+//                            'filename' => Yii::t('kvgrid', 'grid-export'),
+//                            'alertMsg' => Yii::t('kvgrid', 'The CSV export file will be generated for download.'),
+//                            'options' => ['title' => Yii::t('kvgrid', 'Comma Separated Values')],
+//                            'mime' => 'application/csv',
+//                            'config' => [
+//                                'colDelimiter' => ",",
+//                                'rowDelimiter' => "\r\n",
+//                                ],
+//                            ],
+//                        'txt' => [],
+//                        'xls' => [],
+//                        'pdf' => [],
+//                        'json' => [],
+//                    ],
+//                    'panel' => [
+//                        'after' => '',
+//                        'heading' =>  Html::encode($storesMap[$storeId] ?? $storeId) ,
+//                        'type' => 'primary',
+//                        'before' => '',
+//                    ],
+                ]) ?>
+            <?php endforeach; ?>
+        <?php endforeach; ?>
+    <?php endforeach; ?>
+<?php else: ?>
+    <p>Нет данных.</p>
+<?php endif; ?>
+
+
+    <h2>Детальный разбор по всем магазинам</h2>
+    <?php
+
+    $flattenedDebug = [];
+    if (!empty($result['debug'])) {
+        foreach ($result['debug'] as $storeId => $speciesData) {
+            foreach ($speciesData as $species => $saleTypes) {
+                foreach ($saleTypes as $saleType => $entries) {
+                    foreach ($entries as $entry) {
+                        $entry['month'] = $month;
+                        $entry['year']  = $year;
+                        $entry['store_id']       = $storeId;
+                        $entry['store_name']     = isset($storesMap[$storeId]) ? $storesMap[$storeId] : $storeId;
+                        $entry['species']        = $species;
+                        $entry['sale_type']      = $saleType;
+                        $entry['sale_type_label']= isset($saleTypesLabels[$saleType]) ? $saleTypesLabels[$saleType] : $saleType;
+                        $flattenedDebug[] = $entry;
+                    }
+                }
+            }
+        }
+    }
+    $debugProvider = new ArrayDataProvider([
+        'allModels'  => $flattenedDebug,
+        'pagination' => false,
+    ]);
+    $gridColumns = [
+        ['attribute' => 'month',           'label' => 'Месяц'],
+        ['attribute' => 'year',            'label' => 'Год'],
+            ['attribute' => 'store_name', 'label' => 'Магазин'],
+            ['attribute' => 'species',    'label' => 'Вид продукции'],
+            ['attribute' => 'sale_type_label', 'label' => 'Тип продаж'],
+            [
+                'attribute' => 'product_guid',
+                'label'     => 'Product GUID',
+                'value' => function ($model, $key, $index, $widget) {
+                    $product = \yii_app\records\Products1c::find()->where(['id' => $model['product_guid']])->one();
+                    return $product ? $product->name . " (" . $model['product_guid'] . ")" : $model['product_guid'];
+                }
+            ],
+            [
+                'attribute' => 'bouquet_id',
+                'label'     => 'Букет',
+                'value' => function ($model, $key, $index, $widget) {
+                    $bouquet = \yii_app\records\MatrixBouquetForecast::find()->where(['bouquet_id' => $model['bouquet_id']])->one();
+                    return $bouquet ? $bouquet->bouquet_name : $model['bouquet_id'];
+                }
+            ],
+            ['attribute' => 'price',           'label' => 'Цена'],
+            ['attribute' => 'count',           'label' => 'Кол-во'],
+            ['attribute' => 'forecast',        'label' => 'Прогноз'],
+            ['attribute' => 'raw_calculation', 'label' => 'Базовый расчёт'],
+            ['attribute' => 'rounded',         'label' => 'С наценкой за сборку'],
+        ];
+    $exportMenu = ExportMenu::widget([
+        'dataProvider' => $debugProvider,
+        'columns' => $gridColumns,
+        'exportConfig' => [
+            ExportMenu::FORMAT_EXCEL => [
+                'label' => 'Excel',
+                'options' => ['title' => 'Сохранить в Excel'],
+            ],
+            ExportMenu::FORMAT_TEXT => false,
+            ExportMenu::FORMAT_HTML => false,
+            ExportMenu::FORMAT_CSV => false,
+            ExportMenu::FORMAT_PDF => false,
+
+        ],
+        'filename' => 'export_' . date('Y-m-d'),
+        'showColumnSelector' => false,
+        'showConfirmAlert' => false,
+        'target' => ExportMenu::TARGET_SELF,
+        'dropdownOptions' => [
+            'label' => 'Экспорт данных',
+        ],
+    ]);
+    echo $exportMenu;
+    echo GridView::widget([
+        'dataProvider' => $debugProvider,
+        'columns' =>  $gridColumns,
+//        'exportConfig' => [
+//            GridView::CSV => [
+//                'label' => 'CSV',
+//                'icon' => 'glyphicon glyphicon-download',
+//                'iconOptions' => ['class' => 'text-primary'],
+//                'showHeader' => true,
+//                'showPageSummary' => false,
+//                'showFooter' => false,
+//                'filename' => 'debug-export-' . date('Y-m-d_H-i-s'),
+//                'alertMsg' => 'CSV файл будет загружен в ближайшее время.',
+//                'options' => ['title' => 'CSV'],
+//                'mime' => 'application/csv',
+//                'config' => [
+//                    'colDelimiter' => ",",
+//                    'rowDelimiter' => "\r\n",
+//                ],
+//            ],
+//            // Другие типы экспорта можно отключить, если не нужны:
+//            GridView::HTML => false,
+//            GridView::TEXT => false,
+//            GridView::EXCEL => false,
+//            GridView::PDF => false,
+//            GridView::JSON => false,
+//        ],
+//        'toolbar' => [
+//            '{export}',
+//            '{toggleData}',
+//        ],
+//        'panel' => [
+//            'heading' => 'Детальный разбор (Debug) по всем магазинам',
+//            'type' => GridView::TYPE_PRIMARY,
+//        ],
+    ]);
+
+     ?>
+
+</div>