]> gitweb.erp-flowers.ru Git - erp24_rep/yii-erp24/.git/commitdiff
ERP-359 Создать логику расчета на месяц - сумма продаж вида для автопм
authormarina <m.zozirova@gmail.com>
Fri, 11 Apr 2025 06:47:45 +0000 (09:47 +0300)
committermarina <m.zozirova@gmail.com>
Fri, 11 Apr 2025 06:47:45 +0000 (09:47 +0300)
erp24/actions/shipment/AutoPlanogrammaAction.php [deleted file]
erp24/controllers/AutoPlanogrammaController.php [new file with mode: 0644]
erp24/controllers/ShipmentController.php
erp24/services/AutoPlannogrammaService.php [new file with mode: 0644]
erp24/views/auto-planogramma/index.php [new file with mode: 0644]
erp24/views/auto-planogramma/test-sales.php [new file with mode: 0644]
erp24/views/shipment/auto-planogramma.php [deleted file]

diff --git a/erp24/actions/shipment/AutoPlanogrammaAction.php b/erp24/actions/shipment/AutoPlanogrammaAction.php
deleted file mode 100644 (file)
index 4b8737d..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-<?php
-
-namespace yii_app\actions\shipment;
-
-use yii\base\Action;
-use yii\data\ArrayDataProvider;
-use yii\db\Expression;
-use yii\db\Query;
-use yii\helpers\ArrayHelper;
-use yii_app\records\CityStore;
-use yii_app\records\Products1cNomenclature;
-
-
-class AutoPlanogrammaAction extends Action
-{
-    public function run()
-    {
-        $categories = (new Query())
-            ->select([
-                new Expression("
-            CASE 
-                WHEN category ILIKE 'срезка' THEN 'Срезка'
-                WHEN category ILIKE 'горшечные растения' THEN 'Горшечные растения'
-                WHEN category ILIKE 'сухоцветы' THEN 'Сухоцветы'
-                ELSE 'Остальные категории' 
-            END AS category
-        "),
-                'subcategory',
-                new Expression('json_agg(name) AS products')
-            ])
-            ->from('products_1c_nomenclature')
-            ->groupBy(['category', 'subcategory'])
-            ->orderBy([
-                new Expression("
-            CASE 
-                WHEN category ILIKE 'срезка' THEN 1
-                WHEN category ILIKE 'горшечные растения' THEN 2
-                WHEN category ILIKE 'сухоцветы' THEN 3
-                ELSE 4
-            END
-        "),
-                'subcategory' => SORT_ASC
-            ])
-            ->all();
-
-        $stores = ArrayHelper::map(CityStore::findAll(['visible' => CityStore::IS_VISIBLE]), 'id', 'name');
-        //1 month_category_share
-        //-- select * from sales where date >= '2025-03-01'
-        //-- select * from sales_products limit 6
-
-        //1 =1.88
-        //weight./category - не поняла зачем в формуле
-//        SELECT
-//            category,
-//            SUM(sp.summ) AS total_sum,
-//            ROUND((SUM(sp.summ) * 100) /
-//                (SELECT SUM(sp2.summ)
-//                   FROM sales
-//                   LEFT JOIN sales_products sp2 ON sp2.check_id = sales.id
-//                   WHERE date >= '2025-04-01'), 2) AS share_of_total
-//        FROM sales
-//        LEFT JOIN sales_products sp ON sp.check_id = sales.id
-//        LEFT JOIN products_1c_nomenclature p1c ON p1c.id = sp.product_id
-//        WHERE date >= '2025-04-01' and  category = 'Горшечные_растения'
-//        GROUP BY category;
-        //откуда берем 4200000?
-        //2 1.88 * 4200000 = 78960
-        //
-        //3
-//        WITH monthly_sales AS (
-//    SELECT
-//        EXTRACT(MONTH FROM s.date) AS month,
-//        EXTRACT(YEAR FROM s.date) AS year,
-//        SUM(sp.summ) AS total_sales
-//    FROM sales_products sp
-//    LEFT JOIN sales s ON s.id = sp.check_id
-//    WHERE sp.summ IS NOT NULL
-//    GROUP BY EXTRACT(YEAR FROM s.date), EXTRACT(MONTH FROM s.date)
-//),
-//subcategory_sales AS (
-//    SELECT
-//        EXTRACT(MONTH FROM s.date) AS month,
-//        EXTRACT(YEAR FROM s.date) AS year,
-//        p1c.subcategory,
-//        SUM(sp.summ) AS total_sum
-//    FROM sales_products sp
-//    LEFT JOIN products_1c_nomenclature p1c ON p1c.id = sp.product_id
-//    LEFT JOIN sales s ON s.id = sp.check_id
-//    WHERE p1c.category = 'Горшечные_растения'
-//    GROUP BY EXTRACT(YEAR FROM s.date), EXTRACT(MONTH FROM s.date), p1c.subcategory
-//)
-//SELECT
-//    ss.subcategory,
-//    ss.year,
-//    ss.month,
-//    ss.total_sum,
-//    ROUND(
-//        (SUM(ss.total_sum) OVER (PARTITION BY ss.month, ss.subcategory) /
-//         SUM(ss.total_sum) OVER (PARTITION BY ss.month)) * 100,
-//        2
-//    ) AS percent_of_month
-//FROM subcategory_sales ss
-//JOIN monthly_sales ms
-//    ON ss.month = ms.month AND ss.year = ms.year
-//        -- WHERE ss.month = 1  -- Фильтрация только для января
-//ORDER BY ss.month, ss.subcategory, ss.year;
-        //4 = 40.59 * 78960 = 32049
-//        5WITH monthly_sales AS (
-//            SELECT
-//                EXTRACT(MONTH FROM s.date) AS month,
-//                SUM(sp.summ) AS total_sales
-//            FROM erp24.sales_products sp
-//            LEFT JOIN erp24.sales s ON s.id = sp.check_id
-//            WHERE sp.summ IS NOT NULL
-//            GROUP BY EXTRACT(MONTH FROM s.date)
-//        ),
-//        subcategory_sales AS (
-//            SELECT
-//                p1.name,
-//                p1.subcategory,
-//                EXTRACT(MONTH FROM s.date) AS month,
-//                SUM(sp.summ) AS total_sum
-//            FROM erp24.sales_products sp
-//            LEFT JOIN erp24.products_1c_nomenclature p1 ON p1.id = sp.product_id
-//            LEFT JOIN erp24.sales s ON s.id = sp.check_id
-//            WHERE p1.subcategory ILIKE '%Лиственные%'
-//            GROUP BY p1.name, p1.subcategory, EXTRACT(MONTH FROM s.date)
-//        )
-//        SELECT
-//            ss.name,
-//            ss.subcategory,
-//            ss.month,
-//            ss.total_sum,
-//            ROUND(
-//                (ss.total_sum / SUM(ss.total_sum) OVER (PARTITION BY ss.month)) * 100,
-//                2
-//            ) AS percent_of_month
-//        FROM subcategory_sales ss
-//        JOIN monthly_sales ms
-//            ON ss.month = ms.month
-//        ORDER BY ss.month, ss.subcategory, ss.name;
-
-
-
-        return $this->controller->render('auto-planogramma', [
-            'categories' => $categories,
-            'stores' => $stores
-        ]);
-    }
-}
diff --git a/erp24/controllers/AutoPlanogrammaController.php b/erp24/controllers/AutoPlanogrammaController.php
new file mode 100644 (file)
index 0000000..f1eacae
--- /dev/null
@@ -0,0 +1,111 @@
+<?php
+
+namespace app\controllers;
+
+use yii\data\ArrayDataProvider;
+use yii\db\Expression;
+use yii\db\Query;
+use yii\helpers\ArrayHelper;
+use yii_app\records\CityStore;
+use yii_app\services\AutoPlannogrammaService;
+
+class AutoPlanogrammaController extends BaseController
+{
+
+
+    public function behaviors()
+    {
+        return [
+            'access' => [
+                'class' => \yii\filters\AccessControl::class,
+                'rules' => [
+                    [
+                        'allow' => true,
+                        'roles' => ['@'],
+                    ],
+                ],
+            ],
+        ];
+    }
+
+
+    public function actionIndex() {
+        $categories = (new Query())
+            ->select([
+                new Expression("
+            CASE 
+                WHEN category ILIKE 'срезка' THEN 'Срезка'
+                WHEN category ILIKE 'горшечные растения' THEN 'Горшечные растения'
+                WHEN category ILIKE 'сухоцветы' THEN 'Сухоцветы'
+                ELSE 'Остальные категории' 
+            END AS category
+        "),
+                'subcategory',
+                new Expression('json_agg(name) AS products')
+            ])
+            ->from('products_1c_nomenclature')
+            ->groupBy(['category', 'subcategory'])
+            ->orderBy([
+                new Expression("
+            CASE 
+                WHEN category ILIKE 'срезка' THEN 1
+                WHEN category ILIKE 'горшечные растения' THEN 2
+                WHEN category ILIKE 'сухоцветы' THEN 3
+                ELSE 4
+            END
+        "),
+                'subcategory' => SORT_ASC
+            ])
+            ->all();
+
+        $stores = ArrayHelper::map(CityStore::findAll(['visible' => CityStore::IS_VISIBLE]), 'id', 'name');
+
+        return $this->render('index', [
+            'stores' => $stores,
+            'categories' => $categories
+        ]);
+    }
+
+    public function actionTestSales()
+    {
+        $dateFrom = '2024-03-01';
+        $datePlan = '2025-03-01';
+        // Создаём экземпляр сервиса
+        $autoPlannogrammaService = new AutoPlannogrammaService();
+
+        // Шаг 1: Получаем долю по категориям
+        $monthCategoryShare = $autoPlannogrammaService->getMonthCategoryShare($dateFrom);
+
+        // Шаг 2: Получаем цели по категориям
+        $monthCategoryGoal = $autoPlannogrammaService->getMonthCategoryGoal($monthCategoryShare, $datePlan);
+
+        // Шаг 3: Получаем долю по подкатегориям
+        $monthSubcategoryShare = $autoPlannogrammaService->getMonthSubcategoryShare($dateFrom, $datePlan);
+
+        // Шаг 4: Получаем цели по подкатегориям
+        $monthSubcategoryGoal = $autoPlannogrammaService->getMonthSubcategoryGoal($monthSubcategoryShare, $monthCategoryGoal);
+
+        // Шаг 5: Получаем долю по видам
+        $monthSpeciesShare = $autoPlannogrammaService->getMonthSpeciesShare($datePlan);
+
+        // Шаг 6: Получаем цели по видам (final result)
+        $monthSpeciesGoalDirtry = $autoPlannogrammaService->getMonthSpeciesGoalDirtry($monthSpeciesShare, $monthSubcategoryGoal);
+
+        $dataProvider = new ArrayDataProvider([
+            'allModels' => $monthSpeciesGoalDirtry,
+            'pagination' => [
+                'pageSize' => 100
+            ]
+        ]);
+
+        return $this->render('test-sales', [
+            'dataProvider' => $dataProvider,
+        ]);
+    }
+
+    public function actionTestWriteOffs()
+    {
+        return $this->render('test-write-offs');
+    }
+
+}
\ No newline at end of file
index 02f7e87b077234b54e013c48578562f363777a0c..ba40173d4bc854bc6826896a38e96fceda9a6b21 100755 (executable)
@@ -11,7 +11,6 @@ class ShipmentController extends Controller
     public function actions() {
         return [
             'add' => \yii_app\actions\shipment\AddAction::class,
-            'auto-planogramma' => \yii_app\actions\shipment\AutoPlanogrammaAction::class,
         ];
     }
 
diff --git a/erp24/services/AutoPlannogrammaService.php b/erp24/services/AutoPlannogrammaService.php
new file mode 100644 (file)
index 0000000..7fecad0
--- /dev/null
@@ -0,0 +1,253 @@
+<?php
+
+
+namespace yii_app\services;
+
+use yii\db\Expression;
+use yii\db\Query;
+use yii_app\records\Sales;
+use yii_app\records\SalesWriteOffsPlan;
+
+class AutoPlannogrammaService
+{
+    // шаг 1
+    public function getMonthCategoryShare($dateFrom)
+    {
+        $subQuery = (new Query())
+            ->from('sales')
+            ->leftJoin('sales_products sp2', 'sp2.check_id = sales.id')
+            ->where(['>=', 'date', $dateFrom]);
+
+//        if ($isSales !== null) {
+//            $subQuery->andWhere(['sales.is_sale' => $isSales]);
+//        }
+
+        $total = $subQuery->sum('sp2.summ');
+
+        $query = (new Query())
+            ->select([
+                'category',
+                'total_sum' => 'SUM(sp.summ)',
+                'share_of_total' => new Expression(
+                    'ROUND(SUM(sp.summ) / :total, 4)',
+                    [':total' => $total ?: 1]
+                ),
+            ])
+            ->from('sales')
+            ->leftJoin('sales_products sp', 'sp.check_id = sales.id')
+            ->leftJoin('products_1c_nomenclature p1c', 'p1c.id = sp.product_id')
+            ->andWhere(['>=', 'date', $dateFrom])
+            ->andWhere(['<>', 'category', '']);
+
+//        if ($isSales !== null) {
+//            $query->andWhere(['sales.is_sale' => $isSales]);
+//        }
+
+        $query->groupBy('category');
+
+        return $query->all();
+    }
+
+    // шаг 2
+    public function getMonthCategoryGoal(array $monthCategoryShare, $datePlan)
+    {
+        $calculating = [];
+        $timestamp = strtotime($datePlan);
+        $storesPlans = SalesWriteOffsPlan::find()
+            ->andWhere(['year' => date('Y', $timestamp), 'month' => date('m', $timestamp)])
+            ->asArray()
+            ->all();
+        foreach ($storesPlans as $storesPlan) {
+            foreach ($monthCategoryShare as $category) {
+                $calculating[] = [
+                    'category' => $category['category'],
+                    'store_id' => $storesPlan['store_id'],
+                    'goal' => $category['share_of_total'] * $storesPlan['total_sales_plan'],
+                ];
+            }
+        }
+
+        return $calculating;
+    }
+
+    // шаг 3
+    public function getMonthSubcategoryShare($dateFrom, $datePlan)
+    {
+//        $dateFrom = $dateFrom ?? date('Y-m-d', strtotime('-1 year'));
+
+        $monthlySales = (new Query())
+            ->select([
+                'month' => new Expression('EXTRACT(MONTH FROM s.date)'),
+                'year' => new Expression('EXTRACT(YEAR FROM s.date)'),
+                'total_sales' => new Expression('SUM(sp.summ)'),
+            ])
+            ->from(['sp' => 'sales_products'])
+            ->leftJoin(['s' => 'sales'], 's.id = sp.check_id')
+            ->where(['IS NOT', 'sp.summ', null])
+            ->andWhere(['>=', 's.date', $dateFrom])
+            ->groupBy([
+                new Expression('EXTRACT(YEAR FROM s.date)'),
+                new Expression('EXTRACT(MONTH FROM s.date)'),
+            ]);
+
+        $subcategorySales = (new Query())
+            ->select([
+                'month' => new Expression('EXTRACT(MONTH FROM s.date)'),
+                'year' => new Expression('EXTRACT(YEAR FROM s.date)'),
+                'category' => 'p1c.category',
+                'subcategory' => 'p1c.subcategory',
+                'total_sum' => new Expression('SUM(sp.summ)'),
+            ])
+            ->from(['sp' => 'sales_products'])
+            ->leftJoin(['p1c' => 'products_1c_nomenclature'], 'p1c.id = sp.product_id')
+            ->leftJoin(['s' => 'sales'], 's.id = sp.check_id')
+            ->andWhere(['>=', 's.date', $dateFrom])
+            ->groupBy([
+                new Expression('EXTRACT(YEAR FROM s.date)'),
+                new Expression('EXTRACT(MONTH FROM s.date)'),
+                'p1c.category',
+                'p1c.subcategory',
+            ]);
+
+        $query = (new Query())
+            ->select([
+                'ss.category',
+                'ss.subcategory',
+                'ss.year',
+                'ss.month',
+                'ss.total_sum',
+                'percent_of_month' => new Expression(
+                    '(ROUND(SUM(ss.total_sum) OVER (PARTITION BY ss.month, ss.category, ss.subcategory) / SUM(ss.total_sum) OVER (PARTITION BY ss.month, ss.category)), 4)'
+                ),
+            ])
+            ->from(['ss' => $subcategorySales])
+            ->innerJoin(['ms' => $monthlySales], 'ss.month = ms.month AND ss.year = ms.year')
+            ->where(['ss.month' => date('m', strtotime($datePlan))])
+            ->orderBy([
+                'ss.category' => SORT_ASC,
+                'ss.subcategory' => SORT_ASC,
+                'ss.month' => SORT_ASC,
+                'ss.year' => SORT_ASC,
+            ]);
+
+        return $query->all();
+    }
+
+    // шаг 4
+    public function getMonthSubcategoryGoal(array $monthSubcategoryShare, array $monthCategoryGoal)
+    {
+        $calculating = [];
+
+        $goalIndex = [];
+        foreach ($monthCategoryGoal as $goal) {
+            $goalIndex[$goal['category']][] = $goal;
+        }
+//        echo '<pre>';
+//        var_dump($monthSubcategoryShare);
+//        echo '</pre>';
+//        die;
+
+        foreach ($monthSubcategoryShare as $shareData) {
+            if (isset($shareData['category'], $shareData['subcategory'], $shareData['percent_of_month'])) {
+                if (isset($goalIndex[$shareData['category']])) {
+                    foreach ($goalIndex[$shareData['category']] as $goal) {
+                        if (isset($goal['store_id'], $goal['goal'])) {
+                            $calculating[] = [
+                                'category'   => $shareData['category'],
+                                'subcategory'=> $shareData['subcategory'],
+                                'store_id'   => $goal['store_id'],
+                                'goal'       => $goal['goal'] * floatval(strtr($shareData['percent_of_month'], [',' => '.', '(' => '', ')' => ''])),
+                            ];
+                        }
+                    }
+                }
+            }
+        }
+
+        return $calculating;
+    }
+
+    // шаг 5
+    public function getMonthSpeciesShare($datePlan)
+    {
+        $monthlySales = (new Query())
+            ->select([
+                'month' => new Expression('EXTRACT(MONTH FROM s.date)'),
+                'total_sales' => new Expression('SUM(sp.summ)'),
+            ])
+            ->from(['sp' => 'sales_products'])
+            ->leftJoin(['s' => 'sales'], 's.id = sp.check_id')
+            ->where(['IS NOT', 'sp.summ', null])
+            ->groupBy(new Expression('EXTRACT(MONTH FROM s.date)'));
+
+        $subcategorySales = (new Query())
+            ->select([
+                'category' => 'p1.category',
+                'name' => 'p1.name',
+                'subcategory' => 'p1.subcategory',
+                'month' => new Expression('EXTRACT(MONTH FROM s.date)'),
+                'total_sum' => new Expression('SUM(sp.summ)'),
+            ])
+            ->from(['sp' => 'sales_products'])
+            ->leftJoin(['p1' => 'products_1c_nomenclature'], 'p1.id = sp.product_id')
+            ->leftJoin(['s' => 'sales'], 's.id = sp.check_id')
+            ->groupBy(['p1.category', 'p1.name', 'p1.subcategory', new Expression('EXTRACT(MONTH FROM s.date)')]);
+
+        if ($datePlan !== null) {
+            $month = date('m', strtotime($datePlan));
+            $monthlySales->andWhere(['EXTRACT(MONTH FROM s.date)' => $month]);
+            $subcategorySales->andWhere(['EXTRACT(MONTH FROM s.date)' => $month]);
+        }
+
+        $query = (new Query())
+            ->select([
+                'ss.category',
+                'ss.name',
+                'ss.subcategory',
+                'ss.month',
+                'ss.total_sum',
+                'percent_of_month' => new Expression(
+                    'ROUND(ss.total_sum / SUM(ss.total_sum) OVER (PARTITION BY ss.month), 6)'
+                ),
+            ])
+            ->from(['ss' => $subcategorySales])
+            ->innerJoin(['ms' => $monthlySales], 'ss.month = ms.month')
+            ->orderBy([
+                'ss.month' => SORT_ASC,
+                'ss.category' => SORT_ASC,
+                'ss.subcategory' => SORT_ASC,
+                'ss.name' => SORT_ASC,
+            ]);
+
+        return $query->all();
+    }
+
+    // шаг 6
+    public function getMonthSpeciesGoalDirtry(array $monthSpeciesShare, array $monthSubcategoryGoal)
+    {
+        $calculating = [];
+
+        foreach ($monthSpeciesShare as $species) {
+            foreach ($monthSubcategoryGoal as $goalItem) {
+                if (
+                    $species['category'] === $goalItem['category'] &&
+                    $species['subcategory'] === $goalItem['subcategory']
+                ) {
+                    $percent = floatval(strtr($species['percent_of_month'], [',' => '.', '(' => '', ')' => '']));
+                    $goalValue = floatval($goalItem['goal']);
+                    $result = $percent * $goalValue;
+
+                    $calculating[] = [
+                        'category'    => $species['category'],
+                        'subcategory' => $species['subcategory'],
+                        'species'     => $species['name'],
+                        'store_id'    => $goalItem['store_id'],
+                        'goal'        => $result,
+                    ];
+                }
+            }
+        }
+
+        return $calculating;
+    }
+}
diff --git a/erp24/views/auto-planogramma/index.php b/erp24/views/auto-planogramma/index.php
new file mode 100644 (file)
index 0000000..d330c0b
--- /dev/null
@@ -0,0 +1,350 @@
+<?php
+
+use kartik\grid\GridView;
+use kartik\select2\Select2;
+use yii\helpers\ArrayHelper;
+use yii\helpers\Html;
+use yii_app\helpers\DateHelper;
+use yii_app\records\Admin;
+use yii_app\records\AdminGroup;
+use yii_app\records\StoreCityList;
+use yii_app\records\StoreDynamic;
+use yii_app\records\StoreType;
+use sjaakp\bandoneon\Bandoneon;
+use yii\jui\Accordion;
+
+/* @var $categories \yii_app\records\Products1cNomenclature */
+
+
+use yii\data\ArrayDataProvider;
+
+$tooltipText = "Оффлайн: 50&#10;Флаумак (off-line): 20&#10;Флаумак (on-line): 10&#10;Флаумак (market): 10&#10;Изумительная (off-line): 30&#10;Списания: 20"; ?>
+
+<h1 class="ms-3 mb-4"><?= Html::encode("Автопланограмма") ?></h1>
+<div class="autopolnogramma p-3 px-4">
+    <div class="filters">
+        <div class="row">
+        </div>
+        <div class="collapse show" id="filter-container">
+            <div class="row">
+                <div class="col-md d-flex">
+                    <?= Select2::widget([
+                        'name' => 'year-filter',
+                        'data' => array_combine(range(date('Y') - 5, date('Y') + 5), range(date('Y') - 5, date('Y') + 5)),
+                        'options' => ['placeholder' => 'Год', 'id' => 'year'],
+                        'pluginOptions' => ['allowClear' => true],
+                    ]) ?>
+                </div>
+                <div class="col-md d-flex">
+                    <?= Select2::widget([
+                        'name' => 'city-filter',
+                        'data' => ArrayHelper::map(StoreCityList::findAll(['type' => StoreCityList::TYPE_CITY]), 'id', 'name'),
+                        'options' => ['placeholder' => 'Город', 'id' => 'city'],
+                        'pluginOptions' => ['allowClear' => true],
+                    ]) ?>
+                </div>
+                <div class="col-md d-flex">
+                    <?= Select2::widget([
+                        'name' => 'store-type-filter',
+                        'data' => ArrayHelper::map(StoreType::find()->orderBy('sequence_number')->all(), 'id', 'name'),
+                        'options' => ['placeholder' => 'Тип магазина', 'id' => 'store-type', 'style' => 'width: 110%;'],
+                        'pluginOptions' => ['allowClear' => true],
+                    ]) ?>
+                </div>
+                <div class="col-md d-flex">
+                    <?= Select2::widget([
+                        'name' => 'territorial-manager-filter',
+                        'data' => ArrayHelper::map(
+                            array_merge(
+                                Admin::findAll(['group_id' => AdminGroup::GROUP_BUSH_DIRECTOR]),
+                                Admin::findAll(['id' => StoreDynamic::find()->andWhere(['category' => 3, 'active' => 1])->select('value_int')->column()])
+                            ),
+                            'id',
+                            'name',
+                        ),
+                        'options' => ['placeholder' => 'Тер. управляющий', 'id' => 'territorial-manger'],
+                        'pluginOptions' => ['allowClear' => true],
+                    ]) ?>
+                </div>
+                <div class="col-md d-flex">
+                    <?= Select2::widget([
+                        'name' => 'polnogramma-type-filter',
+                        'data' => ['min' => 'min', 'max' => 'max'],
+                        'options' => ['placeholder' => 'Тип п-ма', 'id' => 'polnogramma-type'],
+                        'pluginOptions' => ['allowClear' => true],
+                    ]) ?>
+                </div>
+                <div class="col-md d-flex">
+                    <?= Html::submitButton('Применить', ['class' => 'btn btn-apply btn-primary', 'style' => 'width:100%']); ?>
+                </div>
+            </div>
+            <div class="row py-3">
+                <div class="col-md d-flex">
+                    <?= Select2::widget([
+                        'name' => 'week-filter',
+                        'data' => ['1' => '1', '2' => '2'],
+                        'options' => ['placeholder' => 'Неделя', 'id' => 'week'],
+                        'pluginOptions' => ['allowClear' => true],
+                    ]) ?>
+                </div>
+                <div class="col-md d-flex">
+                    <?= Select2::widget([
+                        'name' => 'region-filter',
+                        'data' => ArrayHelper::map(StoreCityList::findAll(['type' => StoreCityList::TYPE_REGION]), 'id', 'name'),
+                        'options' => ['placeholder' => 'Регион', 'id' => 'region'],
+                        'pluginOptions' => ['allowClear' => true],
+                    ]) ?>
+                </div>
+                <div class="col-md d-flex">
+                </div>
+                <div class="col-md d-flex">
+                    <?= Select2::widget([
+                        'name' => 'bush_chef_florist',
+                        'value' => null,
+                        'data' => ArrayHelper::map(Admin::findAll(['group_id' => AdminGroup::GROUP_BUSH_CHEF_FLORIST]), 'id', 'name'),
+                        'options' => [
+                            'placeholder' => 'Кустовой шеф-флорист',
+                            'class' => 'form-select',
+                            'id' => 'bush_chef_florist',
+                        ],
+                        'pluginOptions' => [
+                            'allowClear' => true,
+                        ],
+                    ]); ?>
+                </div>
+                <div class="col-md d-flex">
+                </div>
+                <div class="col-md d-flex"></div>
+            </div>
+            <div class="row">
+                <div class="col-md d-flex">
+                    <?= Html::label('Месяца-месяцы') ?>
+                </div>
+                <div class="col-md d-flex">
+                    <?= Select2::widget([
+                        'name' => 'district-filter',
+                        'data' => ArrayHelper::map(StoreCityList::findAll(['type' => StoreCityList::TYPE_DISTRICT]), 'id', 'name'),
+                        'options' => ['placeholder' => 'Район', 'id' => 'district'],
+                        'pluginOptions' => ['allowClear' => true],
+                    ]) ?>
+                </div>
+                <div class="col-md d-flex"></div>
+                <div class="col-md d-flex"></div>
+                <div class="col-md d-flex"></div>
+                <div class="col-md d-flex">
+                    <?= Html::button('Сохранить', ['class' => 'btn btn-success btn-save', 'style' => 'width:100%']); ?>
+                </div>
+            </div>
+            <div class="row">
+                <div class="d-flex justify-content-center">
+                    <button class="btn btn-light" type="button" data-bs-toggle="collapse"
+                            data-bs-target="#filter-container"
+                            aria-expanded="true">
+                        <span data-bs-collapse-icon="open">▲</span>
+                        <span data-bs-collapse-icon="closed">▼</span>
+                    </button>
+                </div>
+            </div>
+        </div>
+    </div>
+    <div class="table-wrapper">
+        <table class="table">
+            <thead>
+            <tr class="head">
+                <th scope="col" style="text-align: left !important;">
+                    <?= Html::label("год: 2025 неделя: 05") ?><br>
+                    <?= Html::label("январь - февраль") ?><br>
+                    <?= Html::label("Тип п-ма:") ?><br>
+                    <?= Html::label("Город:") ?><br>
+                    <?= Html::label("Регион:") ?><br>
+                    <?= Html::label("Район:") ?><br>
+                    <?= Html::label("Тип магазина:") ?><br>
+                    <?= Html::label("Тер. Уп.:") ?><br>
+                    <?= Html::label("КШФ:") ?><br>
+                    <div class="buttons d-flex">
+                        <?= Html::a('Auto', '#', ['class' => 'btn btn-success ml-auto']) ?>
+                        <?= Html::a('Corrected', '#', ['class' => 'btn btn-success ml-auto']) ?>
+                    </div>
+                </th>
+
+                <?php foreach ($stores as $store) { ?>
+                    <th scope="col" class="fixed-column">
+                        <?= Html::label($store, null, [
+                            'style' => 'writing-mode: sideways-lr; text-align: center; white-space: nowrap; 
+                                font-weight: bold; transform-origin: left bottom; padding-right: 7%;'
+                        ]) ?>
+                    </th>
+                <?php } ?>
+            </tr>
+            </thead>
+            <tbody>
+            <?php
+            $groupedCategories = [];
+
+            // Группируем данные по категориям и подкатегориям
+            foreach ($categories as $item) {
+                $groupedCategories[$item['category']][$item['subcategory']] = $item['products'];
+            }
+
+            foreach ($groupedCategories as $category => $subcategories): ?>
+                <tr>
+                    <td class="category">
+                        <a class="list-group-item list-group-item-action">
+                            <?= Html::encode($category) ?> ▲
+                        </a>
+                    </td>
+                </tr>
+
+                <?php foreach ($subcategories as $subcategory => $products): ?>
+                    <tr>
+                        <td style="position: relative; display: flex; justify-content: flex-end;" class="subcategory">
+                            <a class="list-group-item list-group-item-action" style="width: 95%;">
+                                <?= Html::encode($subcategory) ?> ▶
+                            </a>
+                        </td>
+                    </tr>
+                    <?php
+                    $products = is_string($products) ? json_decode($products, true) : $products;
+                    ?>
+                    <?php foreach ($products as $product): ?>
+                        <tr>
+                            <td style="position: relative; display: flex; justify-content: flex-end;">
+                                <a class="list-group-item list-group-item-action" style="width: 90%;">
+                                    <?= Html::encode($product) ?>
+                                </a>
+                            </td>
+
+                            <?php foreach ($stores as $store): ?>
+                                <td class="items">
+                                    <div style="display: flex; align-items: center;">
+                                        <?= Html::input('text', '', 50, [
+                                            'class' => 'btn btn-primary input',
+                                            'data-bs-toggle' => 'tooltip',
+                                            'data-bs-placement' => 'top',
+                                            'title' => $tooltipText,
+                                        ]) ?>
+                                        <button style="border: none; background: transparent; cursor: pointer; margin-left: 5px;">
+                                            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+                                                <path d="M20 11v5a1 1 0 0 1-2 0v-4H7.414l1.293 1.293a1 1 0 0 1-1.414 1.414l-3-3a1 1 0 0 1 0-1.416l3-3a1 1 0 1 1 1.414 1.416L7.414 10H19a1 1 0 0 1 1 1z"
+                                                      fill="grey" stroke="none"/>
+                                            </svg>
+                                        </button>
+                                    </div>
+                                </td>
+                            <?php endforeach; ?>
+                        </tr>
+                    <?php endforeach; ?>
+                <?php endforeach; ?>
+            <?php endforeach; ?>
+            </tbody>
+        </table>
+    </div>
+</div>
+<script>
+    document.addEventListener("DOMContentLoaded", function () {
+        // Изначально скрываем все строки, кроме .category и .head
+        document.querySelectorAll("tr").forEach(row => {
+            if (!row.querySelector(".category") && !row.classList.contains("head")) {
+                row.style.display = "none";
+            }
+        });
+
+        // Обработчик кликов для категорий
+        document.querySelectorAll(".category").forEach(category => {
+            category.addEventListener("click", function () {
+                let isVisible = this.parentElement.nextElementSibling?.style.display === "table-row";
+                let nextRow = this.parentElement.nextElementSibling;
+
+                // Закрываем все вложенные элементы (subcategory + items)
+                while (nextRow && !nextRow.querySelector(".category")) {
+                    nextRow.style.display = "none";
+                    nextRow = nextRow.nextElementSibling;
+                }
+
+                // Если категория закрывалась, то открываем только подкатегории
+                if (!isVisible) {
+                    nextRow = this.parentElement.nextElementSibling;
+                    while (nextRow && !nextRow.querySelector(".category")) {
+                        if (nextRow.querySelector(".subcategory")) {
+                            nextRow.style.display = "table-row";
+                        }
+                        nextRow = nextRow.nextElementSibling;
+                    }
+                }
+            });
+        });
+
+        let activeSubcategory = null; // Текущая открытая подкатегория
+
+        // Обработчик кликов для подкатегорий
+        document.querySelectorAll(".subcategory").forEach(subcategory => {
+            subcategory.addEventListener("click", function () {
+                let isOpening = activeSubcategory !== this;
+
+                // Закрываем текущую открытую подкатегорию и её items
+                if (activeSubcategory) {
+                    let prevRow = activeSubcategory.parentElement.nextElementSibling;
+                    while (prevRow && !prevRow.querySelector(".subcategory") && !prevRow.querySelector(".category")) {
+                        prevRow.style.display = "none";
+                        prevRow = prevRow.nextElementSibling;
+                    }
+                }
+
+                // Если открываем новую, показываем её items
+                if (isOpening) {
+                    let nextRow = this.parentElement.nextElementSibling;
+                    while (nextRow && !nextRow.querySelector(".subcategory") && !nextRow.querySelector(".category")) {
+                        nextRow.style.display = "table-row";
+                        nextRow = nextRow.nextElementSibling;
+                    }
+                    activeSubcategory = this;
+                } else {
+                    activeSubcategory = null;
+                }
+            });
+        });
+    });
+</script>
+
+<style>
+    /* Стили для таблицы */
+    .table {
+        width: 100%;
+        table-layout: fixed; /* Это позволяет фиксировать ширину колонок */
+        overflow-x: auto; /* Это позволяет прокручивать таблицу по горизонтали */
+        border-collapse: collapse; /* Чтобы не было промежутков между ячейками */
+    }
+
+    /* Первая колонка будет фиксированной */
+    .fixed-column {
+        width: 170px; /* Устанавливаем фиксированную ширину для первой колонки */
+        white-space: nowrap; /* Запрещаем перенос текста в ячейке */
+        border-right: 1px solid #ddd; /* Вертикальная линия между колонками */
+    }
+
+    /* Для остальных колонок можно установить фиксированную ширину, если нужно */
+    th, td {
+        width: 300px; /* Пример для остальных колонок */
+        text-align: center; /* Центрируем текст */
+        padding: 10px; /* Немного отступов для улучшения внешнего вида */
+        border-right: 1px solid #ddd; /* Вертикальная линия между колонками */
+    }
+
+    .items .input {
+        width: 100%;
+    }
+
+    .table-wrapper {
+        overflow-x: auto;
+        max-width: 100%;
+        position: relative;
+    }
+
+    .fixed-column {
+        position: sticky;
+        left: 0;
+        background: white;
+        z-index: 2;
+        border-right: 2px solid #ddd;
+    }
+</style>
\ No newline at end of file
diff --git a/erp24/views/auto-planogramma/test-sales.php b/erp24/views/auto-planogramma/test-sales.php
new file mode 100644 (file)
index 0000000..609827b
--- /dev/null
@@ -0,0 +1,92 @@
+<?php
+
+use kartik\select2\Select2;
+use yii\helpers\ArrayHelper;
+use yii\widgets\ActiveForm;
+use yii\helpers\Html;
+use yii\grid\GridView;
+use yii\data\ArrayDataProvider;
+use yii_app\records\Products1cNomenclature;
+
+/** @var array $monthSpeciesGoalDirtry */
+//
+////$categories = ArrayHelper::map(Products1cNomenclature::find()->select('category')->distinct()->all(), 'category', 'category');
+////$subcategories = $categories = ArrayHelper::map(Products1cNomenclature::find()->select('category')->distinct()->all(), 'category', 'subcategory');
+////$speciesList = $categories = ArrayHelper::map(Products1cNomenclature::find()->select('category')->distinct()->all(), 'category', 'species');
+////$stores = $categories = ArrayHelper::map(Products1cNomenclature::find()->select('category')->distinct()->all(), 'category', 'category');
+//
+////$filter = Yii::$app->request->get('filter', []);
+////
+////$filteredData = array_filter($monthSpeciesGoalDirtry, function ($item) use ($filter) {
+////    foreach ($filter as $key => $value) {
+////        if ($value !== '' && $item[$key] != $value) {
+////            return false;
+////        }
+////    }
+////    return true;
+////});
+//
+//$dataProvider = new ArrayDataProvider([
+//    'allModels' => $filteredData,
+//    'pagination' => [
+//        'pageSize' => 20,
+//    ],
+//]);
+//
+//?>
+<!---->
+<?php //$form = ActiveForm::begin(['method' => 'get']); ?>
+<!--<div style="display: flex; flex-wrap: wrap; gap: 15px; margin-bottom: 20px;">-->
+<!---->
+<!--    --><?php //= Select2::widget([
+//        'name' => 'filter[category]',
+//        'value' => $filter['category'] ?? '',
+//        'data' => ArrayHelper::map($categories, fn($v) => $v, fn($v) => $v),
+//        'options' => ['placeholder' => 'Категория'],
+//        'pluginOptions' => ['allowClear' => true],
+//    ]) ?>
+<!---->
+<!--    --><?php //= Select2::widget([
+//        'name' => 'filter[subcategory]',
+//        'value' => $filter['subcategory'] ?? '',
+//        'data' => ArrayHelper::map($subcategories, fn($v) => $v, fn($v) => $v),
+//        'options' => ['placeholder' => 'Подкатегория'],
+//        'pluginOptions' => ['allowClear' => true],
+//    ]) ?>
+<!---->
+<!--    --><?php //= Select2::widget([
+//        'name' => 'filter[species]',
+//        'value' => $filter['species'] ?? '',
+//        'data' => ArrayHelper::map($speciesList, fn($v) => $v, fn($v) => $v),
+//        'options' => ['placeholder' => 'Вид'],
+//        'pluginOptions' => ['allowClear' => true],
+//    ]) ?>
+<!---->
+<!--    --><?php //= Select2::widget([
+//        'name' => 'filter[store_id]',
+//        'value' => $filter['store_id'] ?? '',
+//        'data' => ArrayHelper::map($stores, fn($v) => $v, fn($v) => 'Магазин ' . $v),
+//        'options' => ['placeholder' => 'Магазин'],
+//        'pluginOptions' => ['allowClear' => true],
+//    ]) ?>
+<!---->
+<!--    --><?php //= Html::submitButton('Фильтровать', ['class' => 'btn btn-primary']) ?>
+<!--    --><?php //= Html::a('Сбросить', ['index'], ['class' => 'btn btn-default']) ?>
+<!---->
+<!--</div>-->
+<?php //ActiveForm::end(); ?>
+
+<?= GridView::widget([
+    'dataProvider' => $dataProvider,
+    'columns' => [
+        'category',
+        'subcategory',
+        'species',
+        'store_id',
+        [
+            'attribute' => 'goal',
+            'format' => ['decimal', 4],
+            'label' => 'План закупок(RUB)',
+        ],
+    ],
+]); ?>
diff --git a/erp24/views/shipment/auto-planogramma.php b/erp24/views/shipment/auto-planogramma.php
deleted file mode 100644 (file)
index d330c0b..0000000
+++ /dev/null
@@ -1,350 +0,0 @@
-<?php
-
-use kartik\grid\GridView;
-use kartik\select2\Select2;
-use yii\helpers\ArrayHelper;
-use yii\helpers\Html;
-use yii_app\helpers\DateHelper;
-use yii_app\records\Admin;
-use yii_app\records\AdminGroup;
-use yii_app\records\StoreCityList;
-use yii_app\records\StoreDynamic;
-use yii_app\records\StoreType;
-use sjaakp\bandoneon\Bandoneon;
-use yii\jui\Accordion;
-
-/* @var $categories \yii_app\records\Products1cNomenclature */
-
-
-use yii\data\ArrayDataProvider;
-
-$tooltipText = "Оффлайн: 50&#10;Флаумак (off-line): 20&#10;Флаумак (on-line): 10&#10;Флаумак (market): 10&#10;Изумительная (off-line): 30&#10;Списания: 20"; ?>
-
-<h1 class="ms-3 mb-4"><?= Html::encode("Автопланограмма") ?></h1>
-<div class="autopolnogramma p-3 px-4">
-    <div class="filters">
-        <div class="row">
-        </div>
-        <div class="collapse show" id="filter-container">
-            <div class="row">
-                <div class="col-md d-flex">
-                    <?= Select2::widget([
-                        'name' => 'year-filter',
-                        'data' => array_combine(range(date('Y') - 5, date('Y') + 5), range(date('Y') - 5, date('Y') + 5)),
-                        'options' => ['placeholder' => 'Год', 'id' => 'year'],
-                        'pluginOptions' => ['allowClear' => true],
-                    ]) ?>
-                </div>
-                <div class="col-md d-flex">
-                    <?= Select2::widget([
-                        'name' => 'city-filter',
-                        'data' => ArrayHelper::map(StoreCityList::findAll(['type' => StoreCityList::TYPE_CITY]), 'id', 'name'),
-                        'options' => ['placeholder' => 'Город', 'id' => 'city'],
-                        'pluginOptions' => ['allowClear' => true],
-                    ]) ?>
-                </div>
-                <div class="col-md d-flex">
-                    <?= Select2::widget([
-                        'name' => 'store-type-filter',
-                        'data' => ArrayHelper::map(StoreType::find()->orderBy('sequence_number')->all(), 'id', 'name'),
-                        'options' => ['placeholder' => 'Тип магазина', 'id' => 'store-type', 'style' => 'width: 110%;'],
-                        'pluginOptions' => ['allowClear' => true],
-                    ]) ?>
-                </div>
-                <div class="col-md d-flex">
-                    <?= Select2::widget([
-                        'name' => 'territorial-manager-filter',
-                        'data' => ArrayHelper::map(
-                            array_merge(
-                                Admin::findAll(['group_id' => AdminGroup::GROUP_BUSH_DIRECTOR]),
-                                Admin::findAll(['id' => StoreDynamic::find()->andWhere(['category' => 3, 'active' => 1])->select('value_int')->column()])
-                            ),
-                            'id',
-                            'name',
-                        ),
-                        'options' => ['placeholder' => 'Тер. управляющий', 'id' => 'territorial-manger'],
-                        'pluginOptions' => ['allowClear' => true],
-                    ]) ?>
-                </div>
-                <div class="col-md d-flex">
-                    <?= Select2::widget([
-                        'name' => 'polnogramma-type-filter',
-                        'data' => ['min' => 'min', 'max' => 'max'],
-                        'options' => ['placeholder' => 'Тип п-ма', 'id' => 'polnogramma-type'],
-                        'pluginOptions' => ['allowClear' => true],
-                    ]) ?>
-                </div>
-                <div class="col-md d-flex">
-                    <?= Html::submitButton('Применить', ['class' => 'btn btn-apply btn-primary', 'style' => 'width:100%']); ?>
-                </div>
-            </div>
-            <div class="row py-3">
-                <div class="col-md d-flex">
-                    <?= Select2::widget([
-                        'name' => 'week-filter',
-                        'data' => ['1' => '1', '2' => '2'],
-                        'options' => ['placeholder' => 'Неделя', 'id' => 'week'],
-                        'pluginOptions' => ['allowClear' => true],
-                    ]) ?>
-                </div>
-                <div class="col-md d-flex">
-                    <?= Select2::widget([
-                        'name' => 'region-filter',
-                        'data' => ArrayHelper::map(StoreCityList::findAll(['type' => StoreCityList::TYPE_REGION]), 'id', 'name'),
-                        'options' => ['placeholder' => 'Регион', 'id' => 'region'],
-                        'pluginOptions' => ['allowClear' => true],
-                    ]) ?>
-                </div>
-                <div class="col-md d-flex">
-                </div>
-                <div class="col-md d-flex">
-                    <?= Select2::widget([
-                        'name' => 'bush_chef_florist',
-                        'value' => null,
-                        'data' => ArrayHelper::map(Admin::findAll(['group_id' => AdminGroup::GROUP_BUSH_CHEF_FLORIST]), 'id', 'name'),
-                        'options' => [
-                            'placeholder' => 'Кустовой шеф-флорист',
-                            'class' => 'form-select',
-                            'id' => 'bush_chef_florist',
-                        ],
-                        'pluginOptions' => [
-                            'allowClear' => true,
-                        ],
-                    ]); ?>
-                </div>
-                <div class="col-md d-flex">
-                </div>
-                <div class="col-md d-flex"></div>
-            </div>
-            <div class="row">
-                <div class="col-md d-flex">
-                    <?= Html::label('Месяца-месяцы') ?>
-                </div>
-                <div class="col-md d-flex">
-                    <?= Select2::widget([
-                        'name' => 'district-filter',
-                        'data' => ArrayHelper::map(StoreCityList::findAll(['type' => StoreCityList::TYPE_DISTRICT]), 'id', 'name'),
-                        'options' => ['placeholder' => 'Район', 'id' => 'district'],
-                        'pluginOptions' => ['allowClear' => true],
-                    ]) ?>
-                </div>
-                <div class="col-md d-flex"></div>
-                <div class="col-md d-flex"></div>
-                <div class="col-md d-flex"></div>
-                <div class="col-md d-flex">
-                    <?= Html::button('Сохранить', ['class' => 'btn btn-success btn-save', 'style' => 'width:100%']); ?>
-                </div>
-            </div>
-            <div class="row">
-                <div class="d-flex justify-content-center">
-                    <button class="btn btn-light" type="button" data-bs-toggle="collapse"
-                            data-bs-target="#filter-container"
-                            aria-expanded="true">
-                        <span data-bs-collapse-icon="open">▲</span>
-                        <span data-bs-collapse-icon="closed">▼</span>
-                    </button>
-                </div>
-            </div>
-        </div>
-    </div>
-    <div class="table-wrapper">
-        <table class="table">
-            <thead>
-            <tr class="head">
-                <th scope="col" style="text-align: left !important;">
-                    <?= Html::label("год: 2025 неделя: 05") ?><br>
-                    <?= Html::label("январь - февраль") ?><br>
-                    <?= Html::label("Тип п-ма:") ?><br>
-                    <?= Html::label("Город:") ?><br>
-                    <?= Html::label("Регион:") ?><br>
-                    <?= Html::label("Район:") ?><br>
-                    <?= Html::label("Тип магазина:") ?><br>
-                    <?= Html::label("Тер. Уп.:") ?><br>
-                    <?= Html::label("КШФ:") ?><br>
-                    <div class="buttons d-flex">
-                        <?= Html::a('Auto', '#', ['class' => 'btn btn-success ml-auto']) ?>
-                        <?= Html::a('Corrected', '#', ['class' => 'btn btn-success ml-auto']) ?>
-                    </div>
-                </th>
-
-                <?php foreach ($stores as $store) { ?>
-                    <th scope="col" class="fixed-column">
-                        <?= Html::label($store, null, [
-                            'style' => 'writing-mode: sideways-lr; text-align: center; white-space: nowrap; 
-                                font-weight: bold; transform-origin: left bottom; padding-right: 7%;'
-                        ]) ?>
-                    </th>
-                <?php } ?>
-            </tr>
-            </thead>
-            <tbody>
-            <?php
-            $groupedCategories = [];
-
-            // Группируем данные по категориям и подкатегориям
-            foreach ($categories as $item) {
-                $groupedCategories[$item['category']][$item['subcategory']] = $item['products'];
-            }
-
-            foreach ($groupedCategories as $category => $subcategories): ?>
-                <tr>
-                    <td class="category">
-                        <a class="list-group-item list-group-item-action">
-                            <?= Html::encode($category) ?> ▲
-                        </a>
-                    </td>
-                </tr>
-
-                <?php foreach ($subcategories as $subcategory => $products): ?>
-                    <tr>
-                        <td style="position: relative; display: flex; justify-content: flex-end;" class="subcategory">
-                            <a class="list-group-item list-group-item-action" style="width: 95%;">
-                                <?= Html::encode($subcategory) ?> ▶
-                            </a>
-                        </td>
-                    </tr>
-                    <?php
-                    $products = is_string($products) ? json_decode($products, true) : $products;
-                    ?>
-                    <?php foreach ($products as $product): ?>
-                        <tr>
-                            <td style="position: relative; display: flex; justify-content: flex-end;">
-                                <a class="list-group-item list-group-item-action" style="width: 90%;">
-                                    <?= Html::encode($product) ?>
-                                </a>
-                            </td>
-
-                            <?php foreach ($stores as $store): ?>
-                                <td class="items">
-                                    <div style="display: flex; align-items: center;">
-                                        <?= Html::input('text', '', 50, [
-                                            'class' => 'btn btn-primary input',
-                                            'data-bs-toggle' => 'tooltip',
-                                            'data-bs-placement' => 'top',
-                                            'title' => $tooltipText,
-                                        ]) ?>
-                                        <button style="border: none; background: transparent; cursor: pointer; margin-left: 5px;">
-                                            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-                                                <path d="M20 11v5a1 1 0 0 1-2 0v-4H7.414l1.293 1.293a1 1 0 0 1-1.414 1.414l-3-3a1 1 0 0 1 0-1.416l3-3a1 1 0 1 1 1.414 1.416L7.414 10H19a1 1 0 0 1 1 1z"
-                                                      fill="grey" stroke="none"/>
-                                            </svg>
-                                        </button>
-                                    </div>
-                                </td>
-                            <?php endforeach; ?>
-                        </tr>
-                    <?php endforeach; ?>
-                <?php endforeach; ?>
-            <?php endforeach; ?>
-            </tbody>
-        </table>
-    </div>
-</div>
-<script>
-    document.addEventListener("DOMContentLoaded", function () {
-        // Изначально скрываем все строки, кроме .category и .head
-        document.querySelectorAll("tr").forEach(row => {
-            if (!row.querySelector(".category") && !row.classList.contains("head")) {
-                row.style.display = "none";
-            }
-        });
-
-        // Обработчик кликов для категорий
-        document.querySelectorAll(".category").forEach(category => {
-            category.addEventListener("click", function () {
-                let isVisible = this.parentElement.nextElementSibling?.style.display === "table-row";
-                let nextRow = this.parentElement.nextElementSibling;
-
-                // Закрываем все вложенные элементы (subcategory + items)
-                while (nextRow && !nextRow.querySelector(".category")) {
-                    nextRow.style.display = "none";
-                    nextRow = nextRow.nextElementSibling;
-                }
-
-                // Если категория закрывалась, то открываем только подкатегории
-                if (!isVisible) {
-                    nextRow = this.parentElement.nextElementSibling;
-                    while (nextRow && !nextRow.querySelector(".category")) {
-                        if (nextRow.querySelector(".subcategory")) {
-                            nextRow.style.display = "table-row";
-                        }
-                        nextRow = nextRow.nextElementSibling;
-                    }
-                }
-            });
-        });
-
-        let activeSubcategory = null; // Текущая открытая подкатегория
-
-        // Обработчик кликов для подкатегорий
-        document.querySelectorAll(".subcategory").forEach(subcategory => {
-            subcategory.addEventListener("click", function () {
-                let isOpening = activeSubcategory !== this;
-
-                // Закрываем текущую открытую подкатегорию и её items
-                if (activeSubcategory) {
-                    let prevRow = activeSubcategory.parentElement.nextElementSibling;
-                    while (prevRow && !prevRow.querySelector(".subcategory") && !prevRow.querySelector(".category")) {
-                        prevRow.style.display = "none";
-                        prevRow = prevRow.nextElementSibling;
-                    }
-                }
-
-                // Если открываем новую, показываем её items
-                if (isOpening) {
-                    let nextRow = this.parentElement.nextElementSibling;
-                    while (nextRow && !nextRow.querySelector(".subcategory") && !nextRow.querySelector(".category")) {
-                        nextRow.style.display = "table-row";
-                        nextRow = nextRow.nextElementSibling;
-                    }
-                    activeSubcategory = this;
-                } else {
-                    activeSubcategory = null;
-                }
-            });
-        });
-    });
-</script>
-
-<style>
-    /* Стили для таблицы */
-    .table {
-        width: 100%;
-        table-layout: fixed; /* Это позволяет фиксировать ширину колонок */
-        overflow-x: auto; /* Это позволяет прокручивать таблицу по горизонтали */
-        border-collapse: collapse; /* Чтобы не было промежутков между ячейками */
-    }
-
-    /* Первая колонка будет фиксированной */
-    .fixed-column {
-        width: 170px; /* Устанавливаем фиксированную ширину для первой колонки */
-        white-space: nowrap; /* Запрещаем перенос текста в ячейке */
-        border-right: 1px solid #ddd; /* Вертикальная линия между колонками */
-    }
-
-    /* Для остальных колонок можно установить фиксированную ширину, если нужно */
-    th, td {
-        width: 300px; /* Пример для остальных колонок */
-        text-align: center; /* Центрируем текст */
-        padding: 10px; /* Немного отступов для улучшения внешнего вида */
-        border-right: 1px solid #ddd; /* Вертикальная линия между колонками */
-    }
-
-    .items .input {
-        width: 100%;
-    }
-
-    .table-wrapper {
-        overflow-x: auto;
-        max-width: 100%;
-        position: relative;
-    }
-
-    .fixed-column {
-        position: sticky;
-        left: 0;
-        background: white;
-        z-index: 2;
-        border-right: 2px solid #ddd;
-    }
-</style>
\ No newline at end of file