+++ /dev/null
-<?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
- ]);
- }
-}
--- /dev/null
+<?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
public function actions() {
return [
'add' => \yii_app\actions\shipment\AddAction::class,
- 'auto-planogramma' => \yii_app\actions\shipment\AutoPlanogrammaAction::class,
];
}
--- /dev/null
+<?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;
+ }
+}
--- /dev/null
+<?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 Флаумак (off-line): 20 Флаумак (on-line): 10 Флаумак (market): 10 Изумительная (off-line): 30 Списания: 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
--- /dev/null
+<?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)',
+ ],
+ ],
+]); ?>
+++ /dev/null
-<?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 Флаумак (off-line): 20 Флаумак (on-line): 10 Флаумак (market): 10 Изумительная (off-line): 30 Списания: 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