return ExitCode::OK;
}
+/**
+ public function actionMarinaAutoplannogrammaTest()
+ {
+ $month = date('m');
+ $year = date('Y');
+
+ $plan_date = $year . '-' . str_pad($month, 2, '0', STR_PAD_LEFT) . '-01';
+ $service = new AutoPlannogrammaService();
+ $monthCategoryShare = $service->getMonthCategoryShareOrWriteOff($plan_date);
+ $monthCategoryGoal = $service->getMonthCategoryGoal($monthCategoryShare, $plan_date);
+ $monthSubcategoryShare = $service->getMonthSubcategoryShareOrWriteOff($plan_date);
+ $monthSubcategoryGoal = $service->getMonthSubcategoryGoal($monthSubcategoryShare, $monthCategoryGoal);
+ $monthSpeciesShare = $service->getMonthSpeciesShareOrWriteOff($plan_date);
+ $goals = $service->getMonthSpeciesGoalDirty($monthSpeciesShare, $monthSubcategoryGoal);
+
+ $result = StorePlanService::calculateHistoricalShare(
+ $filters['store_id'],
+ $filters['month'],
+ $filters['year'],
+ $filters['category'],
+ $filters['subcategory'],
+ $filters['species']
+ );
+
+ $noHistoryProductData = $service->calculateSpeciesForecastForProductsWithoutHistory($plan_date);
+
+ $productSalesShare = StorePlanService::calculateProductSalesShareProductsWithHistory(
+ $filters['store_id'],
+ $filters['month'],
+ $result['with_history']
+ );
+
+ $productSalesForecast = $service->calculateProductForecastInPiecesProductsWithHistory(
+ $filters['store_id'],
+ $filters['month'],
+ $productSalesShare,
+ $goals,
+ $filters['subcategory'],
+ $filters['category'],
+ $filters['species']
+ );
+
+ $matrixForecast = MatrixBouquetForecast::find()
+ ->where(['year' => $year, 'month' => $month])
+ ->asArray()
+ ->all();
+ $matrixGroups = array_unique(ArrayHelper::getColumn($matrixForecast, 'group'));
+ $bouquetForecast = StorePlanService::getBouquetSpiecesMonthGoalFromForecast($filters['month'], $filters['year'], $filters['store_id'], $matrixGroups);
+ $speciesData = $bouquetForecast['final'];
+ foreach ($speciesData as $store_id => $categoryData) {
+ foreach ($categoryData as $category => $subcategoryData) {
+ foreach ($subcategoryData as $subcategory => $species) {
+ foreach ($species as $speciesInd => $row) {
+ $bouquetSpeciesForecast[] = [
+ 'category' => $category,
+ 'subcategory' => $subcategory,
+ 'store_id' => $store_id,
+ 'species' => $speciesInd,
+ 'goal' => $row
+ ];
+ }
+ }
+ }
+
+ }
+ $cleanedSpeciesGoals = $service->subtractSpeciesGoals($goals, $bouquetSpeciesForecast, $noHistoryProductData);
+
+
+ $salesProductForecastShare = $service->calculateProductForecastShare($noHistoryProductData, $productSalesForecast);
+
+ $productForecastSpecies = $service->calculateProductSalesBySpecies($salesProductForecastShare, $cleanedSpeciesGoals);
+
+
+ $weeklySales = $service->getHistoricalSpeciesShareByWeek($plan_date);
+
+ $weeklySalesForecast = $service->calculateWeeklyProductForecastPieces($productForecastSpecies, $weeklySales);
+
+
+ $weeklySalesForecastFormated = $service->pivotWeeklyForecast($weeklySalesForecast);
+
+ $flatData = array_filter($weeklySalesForecastFormated, function ($row) use ($filters) {
+ foreach ($filters as $key => $value) {
+ if (empty($value)) continue;
+ if (!isset($row[$key])) continue;
+
+ if (stripos((string)$row[$key], (string)$value) === false) {
+ return false;
+ }
+ }
+ return true;
+ });
+
+ foreach ($flatData as $item) {
+ foreach ($weeks as $weekKey => $weekNumber) {
+ $model = new Autoplannogramma();
+ $model->week = $weekNumber;
+ $model->month = $month;
+ $model->year = $year;
+ $model->product_id = $item['product_id'];
+ $model->store_id = $item['store_id'];
+ $model->quantity = round($item[$weekKey]);
+ $model->quantity_forecast = round($item[$weekKey]);
+ $model->is_archive = false;
+ $model->auto_forecast = true;
+
+ if (!$model->save()) {
+ Yii::error("Ошибка сохранения: " . json_encode($model->errors), __METHOD__);
+ } else {
+ Yii::error("Ошибка сохранения: " . json_encode($model->errors), __METHOD__);
+ }
+ }
+ }
+
+
+ }
+ **/
}
]);
}
- public function actionGetProducts(string $category, string $subcategory, array $filters): array
+ public function actionGetProducts(): array
{
Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
- $models = Autoplannogramma::find()
- ->joinWith('products p')
- ->where(['category' => $category])
- ->andWhere(['subcategory' => $subcategory])
- ->andFilterWhere($filters)
- ->select(['p.id', 'p.name', 'store_id', 'quantity'])
+ $request = Yii::$app->request;
+ $category = $request->get('category');
+ $subcategory = $request->get('subcategory');
+ $filters = $request->get();
+
+ $query = Autoplannogramma::find()
+ ->alias('a')
+ ->leftJoin('products_1c_nomenclature p1n', 'p1n.id = a.product_id')
+ ->leftJoin('city_store_params cp', 'cp.store_id = a.store_id')
+ ->where(['p1n.category' => $category])
+ ->andWhere(['p1n.subcategory' => $subcategory])
+ ->andFilterWhere(['=', 'a.year', $filters['year']])
+ ->andFilterWhere(['=', 'a.week', $filters['week']])
+ ->andFilterWhere(['=', 'cp.address_city', $filters['city']])
+ ->andFilterWhere(['=', 'cp.address_region', $filters['region']])
+ ->andFilterWhere(['=', 'cp.address_district', $filters['district']])
+ ->andFilterWhere(['=', 'cp.store_type', $filters['store_type']])
+ ->andFilterWhere(['=', 'a.capacity_type', $filters['capacity_type']]);
+
+ if (!empty($filters['territorial_manager'])) {
+ $territorialManagerStoreIds = StoreDynamic::find()
+ ->select('store_id')
+ ->where(['category' => 3, 'active' => 1, 'value_int' => $filters['territorial_manager']])
+ ->column();
+ $query->andWhere(['in', 'a.store_id', $territorialManagerStoreIds ?: [-1]]);
+ }
+
+ if (!empty($filters['bush_chef_florist'])) {
+ $bushChefFloristStoreIds = StoreDynamic::find()
+ ->select('store_id')
+ ->where(['category' => 2, 'active' => 1, 'value_int' => $filters['bush_chef_florist']])
+ ->column();
+ $query->andWhere(['in', 'a.store_id', $bushChefFloristStoreIds ?: [-1]]);
+ }
+
+ $models = $query
+ ->select([
+ 'a.id AS plan_id',
+ 'p1n.id AS product_id',
+ 'p1n.name AS product_name',
+ 'a.store_id',
+ 'a.quantity',
+ 'a.month'
+ ])
->asArray()
->all();
$result = [];
foreach ($models as $model) {
- $guid = $model['id'];
- $name = $model['name'];
- $storeId = $model['store_id'];
- $quantity = $model['quantity'];
-
- if (!isset($result[$guid])) {
- $result[$guid] = [
- 'name' => $name,
- 'guid' => $guid,
+ $productId = $model['product_id'];
+ $productName = $model['product_name'];
+
+ if (!isset($result[$productId])) {
+ $result[$productId] = [
+ 'product_id' => $productId,
+ 'name' => $productName,
'values' => [],
];
}
- $result[$guid]['values'][] = [
- 'count' => (int) $quantity,
- 'store_id' => (int) $storeId,
+ $result[$productId]['values'][] = [
+ 'id' => $model['plan_id'],
+ 'quantity' => (int)$model['quantity'],
+ 'store_id' => (int)$model['store_id'],
];
}
return array_values($result);
}
+ public function actionUpdateValues()
+ {
+ Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
+
+ $data = json_decode(Yii::$app->request->getRawBody(), true);
+
+ if (!is_array($data)) {
+ return ['success' => false, 'message' => 'Неверный формат данных'];
+ }
+
+ foreach ($data as $item) {
+ if (isset($item['id'])) {
+ $model = Autoplannogramma::findOne($item['id']);
+ if ($model !== null) {
+ $model->quantity = $item['value'];
+ $model->auto_forecast = false;
+ $model->save(false);
+ }
+ } else {
+ $model = new Autoplannogramma();
+ $model->product_id = $item['product_id'] ?? null;
+ $model->store_id = $item['store_id'] ?? null;
+ $model->year = $item['year'] ?? null;
+ $model->month = $item['month'] ?? null;
+ $model->week = $item['week'] ?? null;
+ $model->quantity = $item['value'];
+ $model->quantity_forecast = 0;
+ $model->auto_forecast = false;
+ $model->save(false);
+ }
+ }
+
+ return ['success' => true];
+ }
+
public function actionGetVisibleStores()
{
Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
}
}
- return ['store_ids' => $q->column()];
+ return array_values($q->column());
}
public function action1()
return true;
});
+
+ echo '<pre>';
+ var_dump($flatData);
+ echo '</pre>';die();
$dataProvider = new ArrayDataProvider([
'allModels' => $flatData,
'pagination' => ['pageSize' => 100],
'id',
'name',
),
- 'options' => ['placeholder' => 'Тер. управляющий', 'id' => 'territorial-manger'],
+ 'options' => ['placeholder' => 'Тер. управляющий', 'id' => 'territorial-manager'],
'pluginOptions' => ['allowClear' => true],
]) ?>
</div>
</div>
<div class="row">
<div class="col-md d-flex">
- <?= Html::label('Месяца-месяцы') ?>
+ <?= Html::label('Месяца-месяцы', [], ['class' => 'month-label']) ?>
</div>
<div class="col-md d-flex">
<?= Select2::widget([
<table class="table">
<thead>
<tr class="head">
- <th scope="col" style="text-align: left !important;">
- <?= Html::label("год: 2025 неделÑ\8f: 05", null, ['class' => 'label-year-week']) ?><br>
- <?= Html::label("январь - февраль", null, ['class' => 'label-month-range']) ?><br>
+ <th scope="col" style="text-align: left !important; display: inline-block; justify-content: flex-end;">
+ <?= Html::label("Ð\93од: Ð\9dеделÑ\8f: ", null, ['class' => 'label-year-week']) ?><br>
+ <?= Html::label("Месяц", null, ['class' => 'label-month-range']) ?><br>
<?= Html::label("Тип п-ма:", null, ['class' => 'label-capacity-type']) ?><br>
<?= Html::label("Город:", null, ['class' => 'label-city']) ?><br>
<?= Html::label("Регион:", null, ['class' => 'label-region']) ?><br>
</div>
</th>
<?php foreach ($stores as $storeId => $storeName): ?>
- <th scope="col" class="fixed-column" data-store-id="<?= $storeId ?>">
+ <th scope="col" class="store-label" data-store-id="<?= $storeId ?>">
<?= Html::label($storeName, null, [
- 'style' => 'text-align: center; white-space: nowrap;
- font-weight: bold; transform-origin: left bottom; padding-right: 7%;'
+ 'style' => 'display: block; white-space: nowrap; margin: auto; font-weight: bold; text-align: center; writing-mode: sideways-lr;'
]) ?>
</th>
<?php endforeach; ?>
<?php foreach ($groupedCategories as $category => $subcategories): ?>
<tr>
- <td class="category">
+ <td class="category" style="display: flex; justify-content: flex-end;">
<a class="list-group-item list-group-item-action">
<?= Html::encode($category) ?> ▲
</a>
</tr>
<?php foreach ($subcategories as $subcategory): ?>
+
<tr>
- <td style="position: relative; display: flex; justify-content: flex-end;" class="subcategory">
- <a href="#" class="list-group-item list-group-item-action subcategory-link"
- style="width: 95%;"
- data-category="<?= Html::encode($category) ?>"
- data-subcategory="<?= Html::encode($subcategory) ?>">
+ <td class="subcategory" style="display: flex; justify-content: flex-end;">
+ <a href="#" class="list-group-item list-group-item-action subcategory-link" style="width: 95%" data-category="<?= Html::encode($category) ?>" data-subcategory="<?= Html::encode($subcategory) ?>">
<?= Html::encode($subcategory) ?> ▶
</a>
</td>
</div>
<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%;
+ width: max-content;
+ table-layout: fixed;
+ border-collapse: collapse;
}
.table-wrapper {
position: relative;
}
- .fixed-column {
- position: sticky;
+ th:first-child,
+ td.category,
+ td.subcategory {
+ position: sticky !important;
left: 0;
- background: white;
- z-index: 2;
+ background: #f8f8fc;
+ z-index: 5;
+ min-width: 300px !important;
+ border-right: 2px solid #ddd;
+ white-space: normal;
+ }
+
+ th:not(:first-child) {
+ position: sticky;
+ top: 0;
+ background: #f8f8fc;
+ z-index: 4;
+ min-width: 150px;
+ white-space: nowrap;
+ text-align: center;
+ font-weight: bold;
+ border-right: 2px solid #ddd;
+ padding: 10px;
+ }
+
+ td:not(:first-child) {
+ min-width: 150px;
+ text-align: center;
+ padding: 10px;
border-right: 2px solid #ddd;
}
</style>
\ No newline at end of file
-document.addEventListener("DOMContentLoaded", function () {
- // Изначально скрываем все строки, кроме .category и .head
- document.querySelectorAll("tr").forEach(row => {
+document.addEventListener("DOMContentLoaded", () => {
+ const rows = document.querySelectorAll("tr");
+ rows.forEach(row => {
if (!row.querySelector(".category") && !row.classList.contains("head")) {
row.style.display = "none";
}
});
- // Обработчик кликов для категорий
document.querySelectorAll(".category").forEach(category => {
category.addEventListener("click", function () {
- let nextRow = this.parentElement.nextElementSibling;
- let isVisible = nextRow?.style.display === "table-row";
+ let row = this.parentElement.nextElementSibling;
+ let isOpen = row?.style.display === "table-row";
- // Закрываем все элементы внутри категории
- while (nextRow && !nextRow.querySelector(".category")) {
- nextRow.style.display = "none";
- nextRow = nextRow.nextElementSibling;
+ while (row && !row.querySelector(".category")) {
+ row.style.display = "none";
+ row = row.nextElementSibling;
}
- // Если категория была закрыта — открываем подкатегории
- if (!isVisible) {
- nextRow = this.parentElement.nextElementSibling;
- while (nextRow && !nextRow.querySelector(".category")) {
- if (nextRow.querySelector(".subcategory")) {
- nextRow.style.display = "table-row";
+ if (!isOpen) {
+ row = this.parentElement.nextElementSibling;
+ while (row && !row.querySelector(".category")) {
+ if (row.querySelector(".subcategory")) {
+ row.style.display = "table-row";
}
- nextRow = nextRow.nextElementSibling;
+ row = row.nextElementSibling;
}
}
});
});
- // Обработчик кликов для подкатегорий
- document.querySelectorAll(".subcategory").forEach(subcategory => {
- subcategory.addEventListener("click", function (event) {
- event.stopPropagation(); // Чтобы клик по подкатегории не закрывал категорию
+ document.querySelectorAll(".subcategory").forEach(sub => {
+ sub.addEventListener("click", function (e) {
+ e.stopPropagation();
+ let row = this.parentElement.nextElementSibling;
+ let isOpen = row?.style.display === "table-row";
- let nextRow = this.parentElement.nextElementSibling;
- let isVisible = nextRow?.style.display === "table-row";
-
- // Переключаем видимость только айтемов внутри этой подкатегории
- while (nextRow && !nextRow.querySelector(".subcategory") && !nextRow.querySelector(".category")) {
- nextRow.style.display = isVisible ? "none" : "table-row";
- nextRow = nextRow.nextElementSibling;
+ while (row && !row.querySelector(".subcategory") && !row.querySelector(".category")) {
+ row.style.display = isOpen ? "none" : "table-row";
+ row = row.nextElementSibling;
}
});
});
$('.subcategory-link').on('click', function (e) {
e.preventDefault();
+ window.getSelection()?.removeAllRanges();
+ this.blur();
+
+ if (!$('#week').val() || !$('#year').val()) {
+ alert('Необходимо выбрать год и неделю для отображения планограммы');
+ return;
+ }
const $link = $(this);
const category = $link.data('category');
const subcategory = $link.data('subcategory');
const $row = $link.closest('tr');
-
- $.ajax({
- url: '/auto-plannogramma/get-products',
- type: 'GET',
- data: {
- category: category,
- subcategory: subcategory
- },
- success: function (response) {
- // Удаляем предыдущие строки
- $row.nextAll('tr.inserted-row').remove();
-
- response.forEach(item => {
- const tr = $('<tr class="inserted-row"></tr>');
-
- // Подкатегория
- const subcategoryTd = $(`
- <td class="subcategory" style="display: flex; justify-content: flex-end;">
- <a href="#" class="list-group-item list-group-item-action subcategory-link"
- style="width: 90%;"
- data-category="${category}" data-subcategory="${subcategory}">
- ${item.name}
- </a>
- </td>
- `);
- tr.append(subcategoryTd);
-
- // Карта store_id => count
- const valuesMap = {};
- item.values.forEach(val => {
- valuesMap[val.store_id] = {
- count: val.count,
- guid: item.guid
- };
- });
-
- // Берём колонку из текущей строки, чтобы соблюсти порядок
- $row.find('td[data-store-id]').each(function () {
- const td = $(this);
- const storeId = td.data('store-id');
- const val = valuesMap[storeId] ?? {count: '', guid: ''};
-
- const newTd = $(`
- <td data-store-id="${storeId}">
- <div style="display: flex; align-items: center;">
- <input type="number"
- class="btn btn-primary input"
- value="${val.count}"
- data-guid="${val.guid}"
- data-store_id="${storeId}"
- data-bs-toggle="tooltip"
- data-bs-placement="top"
- data-original-value="${val.count}"
- style="width: 100%; padding: 0.375rem 0.75rem; font-size: 1rem; line-height: 1.5; border-radius: 0.375rem; border: 1px solid #0d6efd;">
- <button class="reject-btn" 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>
+ const filters = getFilterData();
+ filters.category = category;
+ filters.subcategory = subcategory;
+
+ $.get('/auto-plannogramma/get-products', filters, response => {
+ $row.nextAll('tr.inserted-row').remove();
+
+ response.forEach(item => {
+ const tr = $('<tr class="inserted-row"></tr>');
+
+ const subcategoryTd = $(`
+ <td class="subcategory" style="display: flex; justify-content: flex-end;">
+ <a href="#" class="list-group-item list-group-item-action subcategory-link"
+ style="width: 90%;"
+ data-category="${category}" data-subcategory="${subcategory}">
+ ${item.name}
+ </a>
</td>
`);
+ tr.append(subcategoryTd);
- tr.append(newTd);
- });
-
- $row.after(tr);
+ const valuesMap = Object.fromEntries(item.values.map(val => [
+ val.store_id,
+ {
+ quantity: val.quantity,
+ id: val.id
+ }
+ ]));
+
+ $('table thead th').each(function (index) {
+ const $th = $(this);
+ const storeId = $th.data('store-id');
+
+ if (storeId === undefined) return;
+
+ const isVisible = $(`table tbody tr:first td:eq(${index})`).is(':visible');
+ if (!isVisible) return;
+
+ const val = valuesMap[storeId] || { quantity: '', id: '' };
+
+ const td = $(`
+ <td data-store-id="${storeId}">
+ <div style="display: flex; align-items: center;">
+ <input type="number" class="btn btn-primary input"
+ value="${val.quantity}"
+ data-id="${val.id}"
+ data-guid="${item.product_id}"
+ data-store_id="${storeId}"
+ data-bs-toggle="tooltip" data-bs-placement="top"
+ data-original-value="${val.quantity}"
+ style="width: 100%; padding: .375rem .75rem; font-size: 1rem; line-height: 1.5; border-radius: .375rem; border: 1px solid #0d6efd;">
+ <button class="reject-btn" 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>
+ `);
+
+ tr.append(td);
});
- },
- error: function (xhr) {
- alert('Ошибка: ' + xhr.responseText);
- }
- });
+
+ $row.after(tr);
+ });
+ }).fail(xhr => alert('Ошибка: ' + xhr.responseText));
});
$('#autoplannogramma').on('input', '.input', function () {
- const $input = $(this);
- const newValue = $input.val();
- const $td = $input.closest('td');
- const $svg = $td.find('svg');
-
- $svg.find('path').attr('fill', 'red');
+ $(this).closest('td').find('path').attr('fill', 'red');
});
$('#autoplannogramma').on('click', '.reject-btn', function () {
- const $button = $(this);
- const $td = $button.closest('td');
- const $input = $td.find('input.input');
- const $svg = $td.find('svg');
-
- const originalValue = $input.data('original-value');
-
- if (originalValue !== undefined) {
- $input.val(originalValue);
- $svg.find('path').attr('fill', 'grey');
- }
+ const $input = $(this).closest('td').find('input.input');
+ $input.val($input.data('original-value'));
+ $(this).closest('td').find('path').attr('fill', 'grey');
});
-// Получение значений фильтров
-function getFilterData() {
- return {
- year: $('#year').val(),
- city: $('#city').val(),
- storeType: $('#store-type').val(),
- territorialManager: $('#territorial-manger').val(),
- polnogrammaType: $('#polnogramma-type').val(),
- week: $('#week').val(),
- region: $('#region').val(),
- bushChefFlorist: $('#bush_chef_florist').val(),
- district: $('#district').val(),
- };
-}
-
function getFilterData() {
return {
year: $('#year').val(),
};
}
+function getSelectedText(selector) {
+ const val = $(selector).val();
+ return val ? $(selector).find(`option[value="${val}"]`).text() : '';
+}
+
+function updateFilterLabels(data) {
+ $('.label-year-week').text(`год: ${data.year || ''} неделя: ${data.week || ''}`);
+ $('.label-month-range').text(`Месяц: ${data.month || ''}`);
+ $('.label-capacity-type').text('Тип п-ма: ' + getSelectedText('#polnogramma-type'));
+ $('.label-city').text('Город: ' + getSelectedText('#city'));
+ $('.label-region').text('Регион: ' + getSelectedText('#region'));
+ $('.label-district').text('Район: ' + getSelectedText('#district'));
+ $('.label-store-type').text('Тип магазина: ' + getSelectedText('#store-type'));
+ $('.label-tu').text('Тер. Уп.: ' + getSelectedText('#territorial-manager'));
+ $('.label-kshf').text('КШФ: ' + getSelectedText('#bush_chef_florist'));
+}
+
function applyStoreFilter() {
const data = getFilterData();
- $.get('/auto-plannogramma/get-visible-stores', data, function (response) {
- const allowedStoreIds = (response.store_ids || []).map(String);
+ $.get('/auto-plannogramma/get-visible-stores', data, response => {
+ const allowedStoreIds = (response || []).map(String);
- $('td[data-store-id], th[data-store-id]').each(function () {
- const storeId = String($(this).data('store-id'));
- $(this).toggle(allowedStoreIds.includes(storeId));
- });
-
- // Обновляем значения рядом с лейблами, выводим текст, а не id
- $('.label-year-week').text(`год: ${data.year || '—'} неделя: ${data.week || '—'}`);
- $('.label-month-range').text(getMonthRangeByWeek(data.week));
+ $('[data-store-id]').each(function () {
+ const el = $(this);
+ const storeId = el.data('store-id').toString();
- $('.label-capacity-type').text('Тип п-ма: ' + getSelectedText('#polnogramma-type'));
- $('.label-city').text('Город: ' + getSelectedText('#city'));
- $('.label-region').text('Регион: ' + getSelectedText('#region'));
- $('.label-district').text('Район: ' + getSelectedText('#district'));
- $('.label-store-type').text('Тип магазина: ' + getSelectedText('#store-type'));
- $('.label-tu').text('Тер. Уп.: ' + getSelectedText('#territorial-manager'));
- $('.label-kshf').text('КШФ: ' + getSelectedText('#bush_chef_florist'));
+ const shouldShow = allowedStoreIds.includes(storeId);
+ el.toggle(shouldShow);
+ });
- }).fail(function (xhr) {
+ updateFilterLabels(data);
+ }).fail(xhr => {
console.error('Ошибка при фильтрации магазинов:', xhr.responseText);
});
}
-function getSelectedText(selector) {
- const select = $(selector);
- const val = select.val();
- const text = select.find(`option[value="${val}"]`).text();
- return text;
-}
+$('.btn-save').on('click', function () {
+ const changedValues = [];
+ let hasErrors = false;
-function resetStoreFilter() {
- // Сброс обычных инпутов
- $('#year, #week').val('');
+ $('input.input:visible').each(function () {
+ const $input = $(this);
+ const id = $input.data('id');
+ const value = $input.val();
+ const $arrow = $input.closest('div').find('svg path');
- // Сброс всех Select2
- $('#city, #store-type, #territorial-manager, #polnogramma-type, #region, #bush_chef_florist, #district')
- .val(null)
- .trigger('change');
+ if ($arrow.attr('fill') !== 'red') {
+ return;
+ }
- // Показ всех ячеек магазинов
- $('td[data-store-id], th[data-store-id]').show();
+ if (id) {
+ changedValues.push({
+ id: id,
+ value: value
+ });
+ } else {
+ const product_id = $input.data('guid');
+ const store_id = $input.data('store_id');
+ const year = $('#year').val();
+ const month = $('.month-label').val();
+ const week = $('#week').val();
+ if (!product_id || !store_id || !year || !week) {
+ alert('Для новых записей обязательны: product_id, store_id, year, week.');
+ hasErrors = true;
+ return false;
+ }
- // Очистка лейблов
- $('.label-year-week').text('');
- $('.label-month-range').text('');
- $('.label-city, .label-store-type, .label-tu, .label-capacity-type, .label-region, .label-district, .label-kshf')
- .text('—');
-}
+ changedValues.push({
+ product_id: product_id,
+ store_id: store_id,
+ year: year,
+ month: month,
+ week: week,
+ value: value
+ });
+ }
+ });
-// Привязка событий
-$(document).on('click', '.btn-apply', applyStoreFilter);
-$(document).on('click', '.btn-reset', resetStoreFilter);
-
-// Дополнительная функция: получить диапазон месяцев по номеру недели (примерная логика)
-function getMonthRangeByWeek(week) {
- if (!week) return '—';
- const w = parseInt(week, 10);
- if (w <= 4) return 'январь';
- if (w <= 8) return 'январь - февраль';
- if (w <= 13) return 'февраль - март';
- // ... можно доработать
- return '—';
-}
+ if (hasErrors) return;
+ if (changedValues.length === 0) {
+ alert('Нет изменений для сохранения');
+ return;
+ }
+ $.ajax({
+ url: '/auto-plannogramma/update-values',
+ method: 'POST',
+ contentType: 'application/json',
+ data: JSON.stringify(changedValues),
+ success: function () {
+ alert('Изменения успешно сохранены');
+ $('input.input:visible').each(function () {
+ const $input = $(this);
+ const $arrow = $input.closest('div').find('svg path');
+ if ($arrow.attr('fill') === 'red') {
+ $arrow.attr('fill', 'grey');
+ }
+ });
+ },
+ error: function (xhr) {
+ alert('Ошибка при сохранении: ' + xhr.responseText);
+ }
+ });
+});
+
+function resetStoreFilter() {
+ $('#year, #week').val('');
+ $('#city, #store-type, #territorial-manager, #polnogramma-type, #region, #bush_chef_florist, #district')
+ .val(null).trigger('change');
+
+ $('[data-store-id]').show();
+ updateFilterLabels({});
+}
+
+$('.btn-apply').on('click', function () {
+ applyStoreFilter();
+ $('tr.inserted-row').remove();
+});
+$('.btn-reset').on('click', resetStoreFilter);