]> gitweb.erp-flowers.ru Git - erp24_rep/yii-erp24/.git/commitdiff
Расчет цели
authorfomichev <vladimir.fomichev@erp-flowers.ru>
Thu, 15 May 2025 12:25:43 +0000 (15:25 +0300)
committerfomichev <vladimir.fomichev@erp-flowers.ru>
Thu, 15 May 2025 12:25:43 +0000 (15:25 +0300)
erp24/controllers/BouquetController.php
erp24/migrations/m250514_143555_create_matrix_bouquet_forecast_table.php
erp24/records/MatrixBouquetForecast.php
erp24/services/StorePlanService.php
erp24/views/bouquet/get-guid-bouquet.php

index fdfd9ab0652db2c63df0bc529a14e96b39bbdcf5..6051e805b2ef1b4bec5ab876aa13c2c24a4d78f2 100644 (file)
@@ -6,6 +6,7 @@ use Exception;
 use PhpOffice\PhpSpreadsheet\IOFactory;
 use Yii;
 use yii\base\DynamicModel;
+use yii\data\ActiveDataProvider;
 use yii\data\ArrayDataProvider;
 use yii\db\Expression;
 use yii\filters\AccessControl;
@@ -23,6 +24,7 @@ use yii_app\records\{BouquetComposition,
     CityStore,
     CityStoreParams,
     Files,
+    MatrixBouquetForecast,
     MatrixType,
     PricesDynamic,
     Products1c,
@@ -305,8 +307,6 @@ class BouquetController extends Controller
         $model = new DynamicModel(['excelFile']);
         $model->addRule('excelFile', 'file', ['extensions' => ['xls', 'xlsx'], 'skipOnEmpty' => false]);
 
-        $data = [];
-
         if (Yii::$app->request->isPost) {
             $model->excelFile = UploadedFile::getInstance($model, 'excelFile');
 
@@ -315,30 +315,82 @@ class BouquetController extends Controller
                 $model->excelFile->saveAs($filePath);
 
                 $spreadsheet = IOFactory::load($filePath);
-                $rows = $spreadsheet->getActiveSheet()->toArray();
-
-                $names = array_filter(array_column($rows, 2));
-
-                $products = Products1c::find()
-                    ->select(['id', 'name'])
-                    ->where(['name' => $names])
-                    ->asArray()
-                    ->all();
-
-                $data = $products;
-                //var_dump($products); die();
-                Yii::$app->session->set('productExportData', $data);
+                $rows = $spreadsheet->getActiveSheet()->toArray(null, true, true, true);
+
+                $header = array_shift($rows);
+                foreach ($rows as $row) {
+                    $group = trim($row['B']);
+                    $name = trim($row['C']);
+                    $month = strtolower(trim($row['D']));
+                    $year = (int) date('Y');
+
+                    $newValues = [
+                        's_store'     => (int)$row['E'],
+                        'm_store'     => (int)$row['F'],
+                        'l_store'     => (int)$row['G'],
+                        'xl_store'    => (int)$row['H'],
+                        'marketplace' => (int)$row['I'],
+                        'internet'    => (int)$row['J'],
+                    ];
+
+                    $forecast = MatrixBouquetForecast::find()
+                        ->where([
+                            'bouquet_name'  => $name,
+                            'bouquet_group' => $group,
+                            'year'          => $year,
+                            'month'         => $month,
+                        ])
+                        ->one();
+
+                    if ($forecast) {
+                        $changed = false;
+                        foreach ($newValues as $field => $value) {
+                            if ((int)$forecast->$field !== $value) {
+                                $forecast->$field = $value;
+                                $changed = true;
+                            }
+                        }
+
+                        if ($changed) {
+                            $forecast->updated_at = date('Y-m-d H:i:s');
+                            if (!$forecast->save()) {
+                                Yii::error("Ошибка обновления прогноза: " . json_encode($forecast->getErrors()), __METHOD__);
+                            }
+                        }
+                    } else {
+                        $forecast = new MatrixBouquetForecast();
+                        $forecast->bouquet_name = $name;
+                        $forecast->bouquet_group = $group;
+                        $forecast->year = $year;
+                        $forecast->month = $month;
+                        $forecast->created_at = date('Y-m-d H:i:s');
+                        $forecast->updated_at = date('Y-m-d H:i:s');
+
+                        $product = Products1c::find()->select(['id'])->where(['name' => $name])->one();
+                        $forecast->bouquet_id = $product ? $product->id : null;
+
+                        foreach ($newValues as $field => $value) {
+                            $forecast->$field = $value;
+                        }
+
+                        if (!$forecast->save()) {
+                            Yii::error("Ошибка создания прогноза: " . json_encode($forecast->getErrors()), __METHOD__);
+                        }
+                    }
+                }
+
+                Yii::$app->session->setFlash('success', 'Импорт завершён: записи добавлены или обновлены.');
             }
         }
 
-        $provider = new ArrayDataProvider([
-            'allModels' => $data,
-            'pagination' => false,
+        $dataProvider = new ActiveDataProvider([
+            'query' => MatrixBouquetForecast::find()->orderBy(['id' => SORT_ASC]),
+            'pagination' => ['pageSize' => 200],
         ]);
 
         return $this->render('get-guid-bouquet', [
             'model' => $model,
-            'provider' => $provider,
+            'dataProvider' => $dataProvider,
         ]);
     }
 
index 213112e8bc3c0bafb80ec7ff90bbc545b97627f2..e730e3988b66d3da79f3f9bec26b8388c8d3cf34 100644 (file)
@@ -18,7 +18,7 @@ class m250514_143555_create_matrix_bouquet_forecast_table extends Migration
         if (!isset($tableSchema)) {
             $this->createTable(self::TABLE_NAME, [
                 'id' => $this->primaryKey(),
-                'bouquet_id' => $this->integer()->comment('ИД букета'),
+                'bouquet_id' => $this->string()->comment('ИД букета'),
                 'bouquet_name' => $this->string()->comment('Наименование букета'),
                 'bouquet_group' => $this->string()->comment('Матрица букета'),
                 'year' => $this->integer()->comment('Год'),
index aed1e688514d0b947f810a6b8a0a632dccf77f70..9ab692ed537c3d72ae67b93f0730126bc079dc9d 100644 (file)
@@ -8,7 +8,7 @@ use Yii;
  * This is the model class for table "matrix_bouquet_forecast".
  *
  * @property int $id
- * @property int|null $bouquet_id ИД букета
+ * @property string|null $bouquet_id ИД букета
  * @property string|null $bouquet_name Наименование букета
  * @property string|null $bouquet_group Матрица букета
  * @property int|null $year Год
@@ -41,10 +41,10 @@ class MatrixBouquetForecast extends \yii\db\ActiveRecord
     {
         return [
             [['bouquet_id', 'bouquet_name', 'bouquet_group', 'year', 'month', 's_store', 'm_store', 'l_store', 'xl_store', 'marketplace', 'internet', 'created_at', 'updated_at'], 'default', 'value' => null],
-            [['bouquet_id', 'year', 'month', 's_store', 'm_store', 'l_store', 'xl_store', 'marketplace', 'internet'], 'default', 'value' => null],
-            [['bouquet_id', 'year', 'month', 's_store', 'm_store', 'l_store', 'xl_store', 'marketplace', 'internet'], 'integer'],
+            [['year', 'month', 's_store', 'm_store', 'l_store', 'xl_store', 'marketplace', 'internet'], 'default', 'value' => null],
+            [['year', 'month', 's_store', 'm_store', 'l_store', 'xl_store', 'marketplace', 'internet'], 'integer'],
             [['created_at', 'updated_at'], 'safe'],
-            [['bouquet_name', 'bouquet_group'], 'string', 'max' => 255],
+            [['bouquet_id', 'bouquet_name', 'bouquet_group'], 'string', 'max' => 255],
         ];
     }
 
index d7e3e4111f58ce3c1f36cc3c062d48419c858391..97b8ddccd5dfca91339ee01ceee8a860b948eb10 100755 (executable)
@@ -12,6 +12,7 @@ use yii_app\records\BouquetCompositionProducts;
 use yii_app\records\BouquetForecast;
 use yii_app\records\CityStore;
 use yii_app\records\CityStoreParams;
+use yii_app\records\MatrixBouquetForecast;
 use yii_app\records\Motivation;
 use yii_app\records\PricesDynamic;
 use yii_app\records\Products1cAdditionalCharacteristics;
@@ -909,5 +910,131 @@ class StorePlanService
     }
 
 
+    public static function getBouquetSpiecesMonthGoalFromForecast($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'
+        );
+
+        // Все прогнозы на месяц
+        $forecasts = MatrixBouquetForecast::find()
+            ->where(['year' => $year, 'month' => $month])
+            ->andWhere(['not', ['bouquet_id' => null]])
+            ->asArray()
+            ->all();
+
+        $resultData = [];
+        $debugData = [];
+
+        foreach ($forecasts as $forecast) {
+            $products = [];
+
+            $product = Products1c::find()
+                ->select(['components'])
+                ->where(['id' => $forecast['bouquet_id']])
+                ->asArray()
+                ->one();
+
+            if ($product && !empty($product['components'])) {
+                $components = json_decode($product['components'], true);
+                if (is_array($components)) {
+                    foreach ($components as $productId => $count) {
+                        $products[] = [
+                            'product_guid' => $productId,
+                            'count' => (int)$count,
+                        ];
+                    }
+                }
+            }
+
+            $productGuids = array_filter(array_column($products, 'product_guid'));
+
+            $types = [
+                's_store' => 'offline',
+                'm_store' => 'offline',
+                'l_store' => 'offline',
+                'xl_store' => 'offline',
+                'marketplace' => 'marketplace',
+                'internet' => 'internet'
+            ];
+
+            foreach ($types as $field => $typeSales) {
+                $typeSalesValue = (int)$forecast[$field];
+                if ($typeSalesValue <= 0) {
+                    continue;
+                }
+
+                foreach ($storesParams as $storeId => $regionId) {
+                    $pricesData = ArrayHelper::map(
+                        PricesDynamic::find()
+                            ->where(['product_id' => $productGuids])
+                            ->andWhere(['active' => 1])
+                            ->andWhere(['region_id' => $regionId])
+                            ->select(['price', 'product_id'])
+                            ->asArray()
+                            ->all(),
+                        'product_id',
+                        'price'
+                    );
+
+                    foreach ($products as $product) {
+                        $productModel = Products1cNomenclature::findOne($product['product_guid']);
+                        $species = $productModel?->species ?? 'Неизвестно';
+                        $basePrice = $pricesData[$product['product_guid']] ?? 0;
+                        $rawCalculation = $basePrice * $product['count'] * $typeSalesValue;
+                        $productCost = round($rawCalculation * 1.15, 2);
+
+                        if (!isset($resultData[$storeId][$species][$typeSales])) {
+                            $resultData[$storeId][$species][$typeSales] = 0;
+                        }
+                        $resultData[$storeId][$species][$typeSales] += $productCost;
+
+                        $debugData[$storeId][$species][$typeSales][] = [
+                            'product_guid' => $product['product_guid'],
+                            'price' => $basePrice,
+                            'count' => $product['count'],
+                            'forecast' => $typeSalesValue,
+                            'bouquet_id' => $forecast['bouquet_id'],
+                            'raw_calculation' => $rawCalculation,
+                            'rounded' => $productCost,
+                        ];
+                    }
+                }
+            }
+        }
+
+        $finalResult = [];
+        foreach ($resultData as $storeId => $speciesData) {
+            foreach ($speciesData as $species => $salesData) {
+                $finalResult[$storeId][$species] = round(array_sum($salesData), 2);
+            }
+        }
+
+        return [
+            'detail' => $resultData,
+            'final'  => $finalResult,
+            'debug'  => $debugData
+        ];
+    }
+
+
 }
 
index 4203b808405af4819ee448b8cd371eed0bea6fc9..c5371230a8d2466813bb432478fc0194ed9ecdc9 100644 (file)
@@ -5,31 +5,45 @@ use yii\helpers\Html;
 use kartik\grid\GridView;
 
 /** @var yii\web\View $this */
-/** @var yii\base\DynamicModel $model */
-/** @var yii\data\ArrayDataProvider $provider */
+/** @var yii\data\ActiveDataProvider $dataProvider */
+/** @var \yii\base\DynamicModel $model */
 
-$this->title = 'Импорт товаров из Excel';
+$this->title = 'Импорт прогноза по букетам';
 ?>
-
+<div class="get-bouquet-guid p-4">
     <h1><?= Html::encode($this->title) ?></h1>
 
-<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?>
+<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]); ?>
 <?= $form->field($model, 'excelFile')->fileInput() ?>
     <div class="form-group">
-        <?= Html::submitButton('Загрузить', ['class' => 'btn btn-primary']) ?>
+        <?= Html::submitButton('Загрузить', ['class' => 'btn btn-success']) ?>
     </div>
-<?php ActiveForm::end() ?>
+<?php ActiveForm::end(); ?>
 
-<?php if ($provider->getCount()): ?>
-    <h2>Результаты поиска</h2>
+<?php if (!empty($dataProvider)): ?>
     <?= GridView::widget([
-        'dataProvider' => $provider,
+        'dataProvider' => $dataProvider,
         'columns' => [
-            ['class' => 'yii\grid\SerialColumn'],
             'id',
-            'name',
+            'bouquet_id',
+            [
+                'attribute' => 'bouquet_group',
+                'label' => 'Группа букета',
+            ],
+            [
+                'attribute' => 'bouquet_name',
+                'label' => 'Название букета',
+            ],
+            'year',
+            'month',
+            's_store',
+            'm_store',
+            'l_store',
+            'xl_store',
+            'marketplace',
+            'internet',
         ],
     ]) ?>
+<?php endif; ?>
 
-    <?= Html::a('📥 Скачать найденные товары', ['download'], ['class' => 'btn btn-success']) ?>
-<?php endif; ?>
\ No newline at end of file
+</div>