]> gitweb.erp-flowers.ru Git - erp24_rep/yii-erp24/.git/commitdiff
Вывод расчетов
authorfomichev <vladimir.fomichev@erp-flowers.ru>
Wed, 16 Apr 2025 09:33:50 +0000 (12:33 +0300)
committerfomichev <vladimir.fomichev@erp-flowers.ru>
Wed, 16 Apr 2025 09:33:50 +0000 (12:33 +0300)
erp24/controllers/BouquetController.php
erp24/services/StorePlanService.php
erp24/views/bouquet/month-goal.php

index 6adf096b67472cd58d66503987de21437ba7fe40..73e366ea393672fbafacdbbd977fffcf223ab377 100644 (file)
@@ -4,6 +4,7 @@ namespace app\controllers;
 
 use Exception;
 use Yii;
+use yii\base\DynamicModel;
 use yii\db\Expression;
 use yii\filters\AccessControl;
 use yii\helpers\ArrayHelper;
@@ -257,8 +258,25 @@ class BouquetController extends Controller
     {
         $request = Yii::$app->request;
 
-        $month = $request->post('month') ? (int)$request->post('month') : 5;
-        $year = $request->post('year') ? (int)$request->post('year') : 2025;
+        $month = $request->post('month', 5);
+        $year = $request->post('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::getBouquetSpiecesMonthGoal($month, $year);
 
@@ -270,6 +288,7 @@ class BouquetController extends Controller
         $storesMap = ArrayHelper::map($stores, 'id', 'name');
 
         return $this->render('month-goal', [
+            'model'     => $model,   // передаём динамическую модель
             'result'    => $result,
             'storesMap' => $storesMap,
             'month'     => $month,
index 4137efd3cb00590aec8f2e95cf88d263380c43e3..4a4c9fb0d9afd094c65a965dfe0fe4ce95f077fe 100755 (executable)
@@ -803,110 +803,111 @@ class StorePlanService
 
     }
 
-    public static function getBouquetSpiecesMonthGoal($month, $year) {
-        $stores = ArrayHelper::map(CityStore::find()->select(['id'])->where(['visible' => CityStore::IS_VISIBLE])->asArray()->all(), 'id', 'id');
-        $storesParams = ArrayHelper::map(CityStoreParams::find()
-            ->select(['store_id', 'address_region'])
-            ->where(['store_id' => array_keys($stores)])
-            ->andWhere(['not', ['address_region' => '']])
-            ->asArray()
-            ->indexBy('store_id')
-            ->all(), 'store_id', 'address_region');
+    public static function getBouquetSpiecesMonthGoal($month, $year)
+    {
+        $stores = ArrayHelper::map(
+            CityStore::find()
+                ->select(['id'])
+                ->where(['visible' => CityStore::IS_VISIBLE])
+                ->asArray()
+                ->all(),
+            'id',
+            'id'
+        );
+        $storesParams = ArrayHelper::map(
+            CityStoreParams::find()
+                ->select(['store_id', 'address_region'])
+                ->where(['store_id' => array_keys($stores)])
+                ->andWhere(['not', ['address_region' => '']])
+                ->asArray()
+                ->indexBy('store_id')
+                ->all(),
+            'store_id',
+            'address_region'
+        );
 
         $matrixTypesIds = StorePlanService::getActiveMatrixTypes();
-
         $storesForecasts = [];
-        // получаем букеты из матрицы
+
         if ($matrixTypesIds) {
             foreach ($matrixTypesIds as $matrixTypeId) {
                 $bouquetsArray = StorePlanService::getBouqetsByDate($month, $year, $matrixTypeId);
-                $forecasts = ArrayHelper::getColumn($bouquetsArray , 'bouquetForecast');
+                $forecasts = ArrayHelper::getColumn($bouquetsArray, 'bouquetForecast');
 
                 foreach ($forecasts as $forecastArray) {
                     if (is_array($forecastArray)) {
                         foreach ($forecastArray as $fc) {
                             $bouquetPrice = 0;
-                            if(isset($storesParams[$fc["type_sales_id"]])){
-                                $bouquetPrice = BouquetCompositionPrice::find()
+                            if (isset($storesParams[$fc["type_sales_id"]])) {
+                                $priceModel = BouquetCompositionPrice::find()
                                     ->where(['bouquet_id' => $fc['bouquet_id']])
-                                    ->andWhere(['region_id' => $storesParams[$fc["type_sales_id"]]])->one()->price;
+                                    ->andWhere(['region_id' => $storesParams[$fc["type_sales_id"]]])
+                                    ->one();
+                                if ($priceModel !== null) {
+                                    $bouquetPrice = $priceModel->price;
+                                }
                             }
-
-                            $storesForecasts[] = array_merge($fc, ['matrixTypeId' => $matrixTypeId], ['price' => $bouquetPrice] );
-
+                            $storesForecasts[] = array_merge($fc, [
+                                'matrixTypeId' => $matrixTypeId,
+                                'price'        => $bouquetPrice
+                            ]);
                         }
                     }
                 }
             }
         }
-        $resultArr = [];
+
+        $resultData = [];
+        $debugData = [];
+        //var_dump($storesParams);die();
         foreach ($storesForecasts as $storeForecast) {
             $products = ArrayHelper::toArray(BouquetCompositionProducts::getCompositionProducts($storeForecast['bouquet_id']));
             $productGuids = array_filter(array_column($products, 'product_guid'));
-            $pricesData = ArrayHelper::map(PricesDynamic::find()
-                ->where(['product_id' => $productGuids])
-                ->andWhere(['active' => 1])
-                ->andWhere(['region_id' => $storesParams[$fc["type_sales_id"]]])
-                ->select(['price', 'product_id'])->asArray()->all(), 'product_id', 'price') ;
-            // var_dump($products); die();
+            $pricesData = ArrayHelper::map(
+                PricesDynamic::find()
+                    ->where(['product_id' => $productGuids])
+                    ->andWhere(['active' => 1])
+                    ->andWhere(['region_id' => $storesParams[$storeForecast["type_sales_id"]]])
+                    ->select(['price', 'product_id'])
+                    ->asArray()
+                    ->all(),
+                'product_id',
+                'price'
+            );
             foreach ($products as $product) {
-                $species = Products1cNomenclature::find()->where(['id' => $product['product_guid']])->one()->species;
-                $productCost = round($pricesData[$product['product_guid']] * $product['count'] * 1.15, 2);
-                if (!isset($resultArr[$storeForecast["type_sales_id"]][$species][$storeForecast["type_sales"]])) {
-                    $resultArr[$storeForecast["type_sales_id"]][$species][$storeForecast["type_sales"]] = 0;
+                $productModel = Products1cNomenclature::find()->where(['id' => $product['product_guid']])->one();
+                $species = ($productModel !== null) ? $productModel->species : 'Неизвестно';
+                $basePrice = isset($pricesData[$product['product_guid']]) ? $pricesData[$product['product_guid']] : 0;
+                $rawCalculation = $basePrice * $product['count'] * $storeForecast["type_sales_value"];
+                $productCost = round($rawCalculation * 1.15, 2);
+
+                if (!isset($resultData[$storeForecast["type_sales_id"]][$species][$storeForecast["type_sales"]])) {
+                    $resultData[$storeForecast["type_sales_id"]][$species][$storeForecast["type_sales"]] = 0;
                 }
-                $resultArr[$storeForecast["type_sales_id"]][$species][$storeForecast["type_sales"]] += $productCost;
-
+                $resultData[$storeForecast["type_sales_id"]][$species][$storeForecast["type_sales"]] += $productCost;
+
+                $debugData[$storeForecast["type_sales_id"]][$species][$storeForecast["type_sales"]][] = [
+                    'product_guid' => $product['product_guid'],
+                    'price'        => $basePrice,
+                    'count'        => $product['count'],
+                    'forecast'     => $storeForecast["type_sales_value"],
+                    'bouquet_id'      => $storeForecast["bouquet_id"],
+                    'raw_calculation' => $rawCalculation,
+                    'rounded'      => $productCost,
+                ];
             }
-
         }
+
         $finalResult = [];
-        foreach ($resultArr as $typeSalesId => $speciesData) {
+        foreach ($resultData as $typeSalesId => $speciesData) {
             foreach ($speciesData as $species => $salesDetails) {
                 $finalResult[$typeSalesId][$species] = round(array_sum($salesDetails), 2);
             }
         }
 
-        return ['detail' => $resultArr, 'final' => $finalResult];
-
-
-
+        return ['detail' => $resultData, 'final' => $finalResult, 'debug' => $debugData];
     }
 
 
-    public static function getBouquetGoal($bouquet) {
-            $onlineStores = BouquetForecast::getStoresList(
-                $bouquet['id'],
-                BouquetForecast::ONLINE_STORES,
-                CityStore::class,
-                ['visible' => CityStore::IS_VISIBLE],
-                $bouquet['forecast_month'],
-                $bouquet['forecast_year']
-            );
-            $marketplaceStores = BouquetForecast::getStoresList(
-                $bouquet['id'],
-                BouquetForecast::MARKETPLACE,
-                CityStore::class,
-                ['visible' => CityStore::IS_VISIBLE],
-                $bouquet['forecast_month'],
-                $bouquet['forecast_year']
-            );
-            $offlineStores = BouquetForecast::getStoresList(
-                $bouquet['id'],
-                BouquetForecast::OFFLINE_STORES,
-                StoreType::class,
-                [],
-                $bouquet['forecast_month'],
-                $bouquet['forecast_year']
-            );
-            return [
-                'onlineStores' => $onlineStores,
-                'marketplaceStores' => $marketplaceStores,
-                'offlineStores' => $offlineStores,
-
-            ];
-}
-
-
 }
 
index 29a078427c26b532414f6ff3624b7c438f61974d..b37b58958cb5791e40539b8a129b6f9e5f25746e 100644 (file)
 <?php
+
+use kartik\grid\GridView;
 use yii\helpers\Html;
 use yii\helpers\Url;
+use yii\widgets\ActiveForm;
+
+use yii\data\ArrayDataProvider;
+
+/* @var $model yii\base\DynamicModel */
+/* @var $result array ['final', 'detail', 'debug'] */
+/* @var $storesMap array */
+/* @var $month int */
+/* @var $year int */
 
-/* @var $result array   // Массив с ключами 'detail' и 'final'
-@var $storesMap array // Маппинг [store_id => store_name]
-@var $month integer
-@var $year integer
- */
+$saleTypesLabels = [
+    1 => 'Оффлайн',
+    2 => 'Интернет магазины',
+    3 => 'Маркетплейсы',
+];
 ?>
-<h1>Цели на месяц: <?= $month ?>/<?= $year ?></h1>
-
-<!-- Форма для выбора месяца и года -->
-<?= Html::beginForm(Url::to(['bouquet/month-goal']), 'post') ?>
-<label for="month">Месяц:</label>
-<?= Html::input('number', 'month', $month, ['min' => 1, 'max' => 12]) ?>
-
-<label for="year">Год:</label>
-<?= Html::input('number', 'year', $year, ['min' => 2000, 'max' => 2100]) ?>
-
-<?= Html::submitButton('Показать', ['class' => 'btn btn-primary']) ?>
-<?= Html::endForm() ?>
-
-<hr>
-
-<!-- Таблица для итоговых сумм ('final') -->
-<h2>Итоговые суммы по магазинам и видам продукции</h2>
-<table border="1" cellpadding="5" cellspacing="0">
-    <thead>
-    <tr>
-        <th>Магазин</th>
-        <th>Вид продукции</th>
-        <th>Сумма</th>
-    </tr>
-    </thead>
-    <tbody>
-    <?php if (!empty($result['final'])): ?>
-        <?php foreach ($result['final'] as $storeId => $speciesData): ?>
-            <?php
-            // Если имя магазина отсутствует в mapping'е – выводим id
-            $storeName = isset($storesMap[$storeId]) ? $storesMap[$storeId] : $storeId;
-            ?>
-            <?php foreach ($speciesData as $species => $sum): ?>
-                <tr>
-                    <td><?= Html::encode($storeName) ?></td>
-                    <td><?= Html::encode($species) ?></td>
-                    <td><?= $sum ?></td>
-                </tr>
-            <?php endforeach; ?>
-        <?php endforeach; ?>
-    <?php else: ?>
-        <tr>
-            <td colspan="3">Нет данных для отображения.</td>
-        </tr>
-    <?php endif; ?>
-    </tbody>
-</table>
-
-<!-- Таблица для подробного расчёта ('detail') -->
-<h2>Подробный расчёт по магазинам</h2>
-<table border="1" cellpadding="5" cellspacing="0">
-    <thead>
-    <tr>
-        <th>Магазин</th>
-        <th>Вид продукции</th>
-        <th>Тип продаж</th>
-        <th>Сумма</th>
-    </tr>
-    </thead>
-    <tbody>
-    <?php if (!empty($result['detail'])): ?>
-        <?php foreach ($result['detail'] as $storeId => $speciesData): ?>
-            <?php
-            // Определяем имя магазина по mapping'у
-            $storeName = isset($storesMap[$storeId]) ? $storesMap[$storeId] : $storeId;
-            ?>
-            <?php foreach ($speciesData as $species => $saleTypes): ?>
-                <?php foreach ($saleTypes as $saleType => $amount): ?>
-                    <tr>
-                        <td><?= Html::encode($storeName) ?></td>
-                        <td><?= Html::encode($species) ?></td>
-                        <td><?= Html::encode($saleType) ?></td>
-                        <td><?= $amount ?></td>
-                    </tr>
-                <?php endforeach; ?>
+<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(['your-controller/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;
+                            },
+                            'label' => 'Product GUID'],
+                        ['attribute' => 'bouquet_id',
+                            'value' => function ($model, $key, $index, $widget) {
+                                return \yii_app\records\BouquetComposition::find()->where(['id' => $model['bouquet_id']])->one()->name;
+                            },
+                            'label' => 'Букет'],
+                        ['attribute' => 'price',            'label' => 'Цена'],
+                        ['attribute' => 'count',            'label' => 'Кол-во'],
+                        ['attribute' => 'forecast',         'label' => 'Прогноз'],
+                        ['attribute' => 'raw_calculation',  'label' => 'Базовый расчёт'],
+                        ['attribute' => 'rounded',          'label' => 'С наценкой за сборку'],
+                    ],
+                ]) ?>
             <?php endforeach; ?>
         <?php endforeach; ?>
-    <?php else: ?>
-        <tr>
-            <td colspan="4">Нет данных для отображения.</td>
-        </tr>
-    <?php endif; ?>
-    </tbody>
-</table>
\ No newline at end of file
+    <?php endforeach; ?>
+<?php else: ?>
+    <p>Нет данных.</p>
+<?php endif; ?>
+
+</div>