From 21ffee19d4f111afa8fdaf53e3c75be58e95797f Mon Sep 17 00:00:00 2001 From: vladfo Date: Fri, 13 Sep 2024 15:23:11 +0300 Subject: [PATCH] =?utf8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20?= =?utf8?q?=D1=80=D0=B5=D0=B4=D0=B0=D0=BA=D1=82=D0=B8=D1=80=D0=BE=D0=B2?= =?utf8?q?=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=BA=D1=83=D1=81=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../controllers/ClusterLinkEditController.php | 318 +++++++++++++++++- erp24/views/cluster_link_edit/_form.php | 4 +- erp24/views/cluster_link_edit/index.php | 98 +++++- erp24/views/cluster_link_edit/view-all.php | 171 ++++++++-- 4 files changed, 534 insertions(+), 57 deletions(-) diff --git a/erp24/controllers/ClusterLinkEditController.php b/erp24/controllers/ClusterLinkEditController.php index ee37d362..236c4f2e 100644 --- a/erp24/controllers/ClusterLinkEditController.php +++ b/erp24/controllers/ClusterLinkEditController.php @@ -3,6 +3,7 @@ namespace app\controllers; use Yii; +use yii\data\ArrayDataProvider; use yii\helpers\ArrayHelper; use yii_app\helpers\DateHelper; use yii_app\records\Admin; @@ -47,33 +48,92 @@ class ClusterLinkEditController extends Controller */ public function actionIndex() { - // Поиск моделей кустов $searchModel = new ClusterSearch(); $dataProvider = $searchModel->search($this->request->queryParams); - // Получаем кустовых директоров из таблицы Admin $clusterManagers = Admin::find() ->select(['name', 'id', 'store_arr']) - ->where(['group_id' => 7, 'group_name' => 'Кустовой директор']) // Условия для группы кустовых директоров - ->indexBy('id') // Индексируем по идентификатору + ->where(['group_id' => 7, 'group_name' => 'Кустовой директор']) + ->indexBy('id') ->all(); - // Данные по динамике магазинов, связанных с кустами - $storeData = StoreDynamic::find() + // Получение параметра 'date' из GET-запроса, если он передан + $currentDate = Yii::$app->request->get('ClusterSearch')['date'] ?? date('Y-m-d'); + $minDate = '2024-09-12'; + + $storeData = ClusterCalendar::find() ->select([ - 'value_int AS cluster_id', - 'string_agg(store_id::text, \',\') AS stores', // Используем string_agg для PostgreSQL - 'COUNT(store_id) AS store_count', + 'cluster_id', + 'string_agg(DISTINCT value_int::text, \',\') AS stores', + 'COUNT(DISTINCT value_int) AS store_count', 'MAX(date_from) AS last_update' ]) - ->where(['active' => 1]) - ->groupBy('value_int') // Группируем по идентификатору куста + ->where(['>=', 'date_from', $minDate]) + ->andWhere(['<=', 'date_from', $currentDate]) + ->andWhere(['>', 'date_to', $currentDate]) + ->groupBy('cluster_id') ->asArray() ->all(); + // Если и их нет, создаем новые записи на основе StoreDynamic + if (empty($storeData)) { + // Данные по динамике магазинов, связанных с кустами + $storeDynamic = StoreDynamic::find() + ->select([ + 'value_int AS cluster_id', + 'string_agg(store_id::text, \',\') AS stores', // Используем string_agg для PostgreSQL + 'COUNT(store_id) AS store_count', + 'MAX(date_from) AS last_update' + ]) + ->where(['active' => 1]) + ->groupBy('value_int') // Группируем по идентификатору куста + ->asArray() + ->all(); + + // Преобразуем данные о магазинах в удобный формат + $storeLists = ArrayHelper::map($storeDynamic, 'cluster_id', 'stores'); // Список магазинов для каждого куста + $storeCounts = ArrayHelper::map($storeData, 'cluster_id', 'store_count'); + $lastUpdates = ArrayHelper::map($storeData, 'cluster_id', 'last_update'); + //var_dump($storeDynamic); + foreach ($storeDynamic as $cluster) { + $clusterId = $cluster['cluster_id']; + $stores = explode(',', $cluster['stores']); + $lastUpdate = $cluster['last_update']; + // var_dump($stores); + foreach ($stores as $storeId) { + $newCalendar = new ClusterCalendar(); + $newCalendar->cluster_id = $clusterId; + $newCalendar->value_type = 'int'; + $newCalendar->value_int = (int) $storeId; + $newCalendar->year = '2024'; + $newCalendar->category_id = 1; + $newCalendar->date_from = $minDate; + $newCalendar->date_to = '2100-01-01'; + $newCalendar->created_admin_id = Yii::$app->user->id; + $newCalendar->save(false); + } + } + + + // Обновляем список магазинов после добавления записей + $storeData = ClusterCalendar::find() + ->select([ + 'cluster_id', + 'string_agg(DISTINCT value_int::text, \',\') AS stores', + 'COUNT(DISTINCT value_int) AS store_count', + 'MAX(date_from) AS last_update' + ]) + ->where(['>=', 'date_from', $minDate]) + ->andWhere(['<=', 'date_from', $currentDate]) + ->andWhere(['>', 'date_to', $currentDate]) + ->groupBy('cluster_id') + ->asArray() + ->all(); + } + //var_dump($storeData); // Преобразуем данные о магазинах в удобный формат $storeLists = ArrayHelper::map($storeData, 'cluster_id', 'stores'); // Список магазинов для каждого куста - + $storeCounts = ArrayHelper::map($storeData, 'cluster_id', 'store_count'); // Инициализируем массив соответствий кустов и менеджеров $clusterToManager = []; $matchedManagers = []; @@ -115,15 +175,31 @@ class ClusterLinkEditController extends Controller } - // Передача данных в представление + // Делаем выборку по кластерам с учетом даты + foreach ($storeData as $store) { + $clusterId = $store['cluster_id']; + $calendar = ClusterCalendar::find() + ->where(['cluster_id' => $clusterId]) + ->andWhere(['<=', 'date_from', $currentDate]) + ->andWhere(['>', 'date_to', $currentDate]) + ->andWhere(['>=', 'date_from', '2024-09-12']) // Ограничение на минимальную дату + ->orderBy(['date_from' => SORT_DESC]) + ->one(); + + if ($calendar) { + $lastUpdates[$clusterId] = $calendar->date_from; + } + } + return $this->render('/cluster_link_edit/index', [ 'searchModel' => $searchModel, 'dataProvider' => $dataProvider, 'clusterManagers' => $clusterManagers, - 'storeCounts' => ArrayHelper::map($storeData, 'cluster_id', 'store_count'), - 'lastUpdates' => ArrayHelper::map($storeData, 'cluster_id', 'last_update'), + 'storeCounts' => $storeCounts, + 'lastUpdates' => $lastUpdates, 'storeLists' => $storeLists, - 'clusterToManager' => $clusterToManager, // Передаем соответствия куста и менеджера + 'clusterToManager' => $clusterToManager, + 'currentDate' => $currentDate, ]); } @@ -190,13 +266,196 @@ class ClusterLinkEditController extends Controller * @return string * @throws NotFoundHttpException if the model cannot be found */ - public function actionViewAll($id) + public function actionViewAll($id, $manager = null, $date = null) { + // Получаем модель кластера по ID + $model = $this->findModel($id); + + // Если $currentDate не передан в запросе, используем текущую дату + $currentDate = $date ?? date('Y-m-d'); + + // Ограничение на минимальную дату для выборки + $dateFrom = '2024-09-12'; + $year = date('Y'); + + // Поиск записей в ClusterCalendar для указанного кластера + $stores = ClusterCalendar::find() + ->where(['cluster_id' => $id]) + ->andWhere(['<=', 'date_from', $currentDate]) // Записи, начиная с указанной или более ранней даты + ->andWhere(['>', 'date_to', $currentDate]) // Записи, где дата окончания больше текущей + ->andWhere(['>=', 'date_from', $dateFrom]) // Дата начала должна быть не раньше 2024-09-12 + ->all(); + + // Получаем список всех кустов для перевода + $clustersList = ArrayHelper::map(Cluster::find()->all(), 'id', 'name'); + + // Получение всех магазинов из таблицы CityStore + $storesData = CityStore::find() + ->select(['id', 'name']) // Выбираем только id и name + ->indexBy('id') // Индексируем результат по id + ->asArray() + ->all(); + + // Создание справочника: id магазина => имя магазина + $storeNames = ArrayHelper::map($storesData, 'id', 'name'); + + // Формируем провайдер данных для отображения магазинов + $dataProvider = new ArrayDataProvider([ + 'allModels' => $stores, + ]); + + // Передача данных в представление return $this->render('/cluster_link_edit/view-all', [ - 'model' => $this->findModel($id), + 'model' => $model, + 'dataProvider' => $dataProvider, + 'clustersList' => $clustersList, + 'currentDate' => $currentDate, // Передаем текущую или выбранную дату + 'clusterManager' => $manager, // Передаем кустового директора + 'storeNames' => $storeNames, // Передаем справочник с именами магазинов ]); } + public function actionMoveStore() + { + $storeId = Yii::$app->request->post('store_id'); + $newClusterId = Yii::$app->request->post('new_cluster_id'); + $currentDate = date('Y-m-d'); + + // Найти запись в ClusterCalendar по store_id и актуальной дате + $calendarEntry = ClusterCalendar::find() + ->where(['id' => $storeId]) + ->andWhere(['>=', 'date_from', '2024-09-12']) + ->andWhere(['date_to' => '2100-01-01']) + ->one(); + + if ($calendarEntry) { + // Обновляем текущую запись — закрываем ее с текущей датой + $calendarEntry->date_to = $currentDate; + $calendarEntry->save(false); + + // Создаем новую запись для нового кластера + $newCalendarEntry = new ClusterCalendar(); + $newCalendarEntry->cluster_id = $newClusterId; + $newCalendarEntry->value_type = 'int'; + $newCalendarEntry->value_int = $calendarEntry->value_int; + $newCalendarEntry->date_from = $currentDate; + $newCalendarEntry->category_id = 1; + $newCalendarEntry->date_to = '2100-01-01'; + $newCalendarEntry->created_admin_id = Yii::$app->user->id; + $newCalendarEntry->save(false); + + // Синхронизируем данные менеджеров после переноса + $this->syncClusterManagers(); + + return $this->asJson(['success' => true]); + } + + return $this->asJson(['success' => false]); + } + + public function actionDeleteStore($id, $cluster_id) + { + $currentDate = date('Y-m-d'); + + // Найти запись в ClusterCalendar по store_id и актуальной дате + $calendarEntry = ClusterCalendar::find() + ->where(['id' => $id, 'cluster_id' => $cluster_id]) + ->andWhere(['>=', 'date_from', '2024-09-12']) + ->andWhere(['date_to' => '2100-01-01']) + ->one(); + + if ($calendarEntry) { + // Закрываем текущую запись + $calendarEntry->date_to = $currentDate; + $calendarEntry->save(false); + + // Синхронизируем данные менеджеров после удаления + $this->syncClusterManagers(); + + Yii::$app->session->setFlash('success', 'Магазин успешно удален из куста.'); + } else { + Yii::$app->session->setFlash('error', 'Не удалось удалить магазин.'); + } + + return $this->redirect(['view-all', 'id' => $cluster_id]); + } + + public function actionAddStore($id) + { + $storeId = Yii::$app->request->post('store_id'); + $currentDate = date('Y-m-d'); + + // Проверить, есть ли уже активная запись для данного магазина + $existingEntry = ClusterCalendar::find() + ->where(['value_int' => $storeId]) + ->andWhere(['>=', 'date_from', '2024-09-12']) + ->andWhere(['date_to' => '2100-01-01']) + ->one(); + + if ($existingEntry) { + // Если есть активная запись, закрываем ее, установив date_to текущей датой + $existingEntry->date_to = $currentDate; + $existingEntry->save(false); + } + + // Создаем новую запись для кластера + $newCalendarEntry = new ClusterCalendar(); + $newCalendarEntry->cluster_id = $id; + $newCalendarEntry->value_type = 'int'; + $newCalendarEntry->value_int = $storeId; + $newCalendarEntry->date_from = $currentDate; + $newCalendarEntry->date_to = '2100-01-01'; + $newCalendarEntry->category_id = 1; + $newCalendarEntry->created_admin_id = Yii::$app->user->id; + $newCalendarEntry->save(false); + + // Синхронизируем данные менеджеров после добавления + $this->syncClusterManagers(); + + Yii::$app->session->setFlash('success', 'Магазин успешно добавлен в куст.'); + + return $this->redirect(['view-all', 'id' => $id]); + } + + protected function syncClusterManagers() + { + // Получаем кустовых директоров из таблицы Admin + $clusterManagers = Admin::find() + ->select(['id', 'store_arr']) + ->where(['group_id' => 7, 'group_name' => 'Кустовой директор']) + ->indexBy('id') + ->all(); + + // Делаем выборку данных из ClusterCalendar + $storeData = ClusterCalendar::find() + ->select(['cluster_id', "string_agg(value_int::text, ',') AS stores"]) + ->where(['date_to' => '2100-01-01']) + ->groupBy('cluster_id') + ->asArray() + ->all(); + + // Преобразуем данные о магазинах в удобный формат + $storeLists = ArrayHelper::map($storeData, 'cluster_id', 'stores'); + + // Сопоставляем менеджеров и кусты по спискам магазинов + foreach ($storeLists as $clusterId => $stores) { + $storeIds = explode(',', $stores); + foreach ($clusterManagers as $manager) { + if (!empty($manager->store_arr)) { + $managerStores = explode(',', $manager->store_arr); + $intersection = array_intersect($storeIds, $managerStores); + + // Если большинство магазинов совпадают, менеджер назначается на куст + if (count($intersection) >= count($storeIds) / 2) { + // Обновляем список магазинов менеджера + $manager->store_arr = implode(',', array_unique(array_merge($managerStores, $storeIds))); + $manager->save(false); + } + } + } + } + } + /** * Updates an existing Cluster model. @@ -411,6 +670,29 @@ class ClusterLinkEditController extends Controller ]); } + public function actionCreate() + { + // Создаем новый объект модели Cluster + $model = new Cluster(); + + // Проверяем, отправлена ли форма и заполнены ли данные + if ($model->load(Yii::$app->request->post()) && $model->validate()) { + // Если валидация успешна, переходим к сохранению + if ($model->save()) { + // Перенаправляем на страницу успешного действия (например, на страницу списка кластеров) + return $this->redirect(['index']); + } else { + // Если возникла ошибка при сохранении, можно добавить сообщение об ошибке + Yii::$app->session->setFlash('error', 'Не удалось сохранить данные.'); + } + } + + + return $this->render('/cluster_link_edit/create', [ + 'model' => $model, + ]); + } + /** * Updates an existing Cluster model. * If update is successful, the browser will be redirected to the 'view' page. diff --git a/erp24/views/cluster_link_edit/_form.php b/erp24/views/cluster_link_edit/_form.php index 49809d2d..c4500bab 100644 --- a/erp24/views/cluster_link_edit/_form.php +++ b/erp24/views/cluster_link_edit/_form.php @@ -17,9 +17,9 @@ use yii\widgets\ActiveForm; - field($model, 'name')->dropDownList($stores) ?> - field($model, 'name')->dropDownList(['maxlength' => true]) ?> + field($model, 'name')->dropDownList($stores) */?> field($model, 'name')->textInput(['maxlength' => true]) ?> + field($model, 'short_name')->textInput(['maxlength' => true]) ?>
'btn btn-success']) ?>
diff --git a/erp24/views/cluster_link_edit/index.php b/erp24/views/cluster_link_edit/index.php index 03f0b7c8..afdc42fc 100644 --- a/erp24/views/cluster_link_edit/index.php +++ b/erp24/views/cluster_link_edit/index.php @@ -3,8 +3,10 @@ use yii\helpers\Html; use yii\helpers\Url; use yii\grid\GridView; +use yii\widgets\ActiveForm; use yii\widgets\Pjax; + /** @var yii\web\View $this */ /** @var yii_app\records\ClusterSearch $searchModel */ /** @var yii\data\ActiveDataProvider $dataProvider */ @@ -13,9 +15,11 @@ use yii\widgets\Pjax; /** @var array $lastUpdates */ /** @var array $storeLists */ /** @var array $clusterToManager */ +/** @var string $currentDate */ $this->title = 'Кусты'; $this->params['breadcrumbs'][] = $this->title; + ?>
@@ -25,8 +29,32 @@ $this->params['breadcrumbs'][] = $this->title; 'btn btn-success']) ?>

+ + + + + +
+ $dataProvider, 'filterModel' => $searchModel, @@ -38,8 +66,7 @@ $this->params['breadcrumbs'][] = $this->title; [ 'label' => 'Количество магазинов', 'value' => function ($model) use ($storeCounts) { - // Отображаем количество магазинов из маппинга $storeCounts - return isset($storeCounts[$model->id]) ? $storeCounts[$model->id] : 0; + return $storeCounts[$model->id] ?? 0; }, ], [ @@ -51,19 +78,71 @@ $this->params['breadcrumbs'][] = $this->title; [ 'label' => 'Дата обновления', 'value' => function ($model) use ($lastUpdates) { - // Отображаем дату последнего обновления из маппинга $lastUpdates return isset($lastUpdates[$model->id]) ? date('d.m.Y', strtotime($lastUpdates[$model->id])) : 'Неизвестно'; }, ], [ 'class' => 'yii\grid\ActionColumn', - 'template' => '{view-all} {update}', + 'template' => '{view-all} {update} {delete}' , + 'contentOptions' => ['style' => 'width: 150px; white-space: nowrap;'], 'buttons' => [ - 'view-all' => function ($url, $model, $key) { - return Html::a('Просмотр', $url, ['class' => 'btn btn-primary']); + 'view-all' => function ($url, $model, $key) use ($clusterToManager, $currentDate) { + $currentDateStr = date('Y-m-d'); + if ($currentDate !== $currentDateStr) { + return Html::a('', [ + 'view-all', + 'id' => $model->id, + 'manager' => $clusterToManager[$model->id] ?? 'Не назначен', + 'date' => $currentDate + ], [ + 'class' => 'btn btn-primary btn-sm', + 'title' => 'Просмотр', + 'data-bs-toggle' => 'tooltip', // Подсказка на иконке + 'data-bs-placement' => 'top', + ]); + } else { + // Если дата запроса не совпадает с текущей датой, возвращаем пустую строку + return ''; + } + + }, + 'update' => function ($url, $model, $key) use ($clusterToManager, $currentDate) { + $currentDateStr = date('Y-m-d'); + // Проверка, является ли текущая дата датой запроса + if ($currentDate === $currentDateStr) { + // Ссылка для редактирования с иконкой "Карандаш" (bi-pencil) + return Html::a('', [ + 'view-all', + 'id' => $model->id, + 'manager' => $clusterToManager[$model->id] ?? 'Не назначен', + 'date' => $currentDate + ], [ + 'class' => 'btn btn-success btn-sm', + 'title' => 'Редактировать', + 'data-bs-toggle' => 'tooltip', + 'data-bs-placement' => 'top', + ]); + } else { + // Если дата запроса не совпадает с текущей датой, возвращаем пустую строку + return ''; + } }, - 'update' => function ($url, $model, $key) { - return Html::a('Редактировать', $url, ['class' => 'btn btn-warning']); + 'delete' => function ($url, $model, $key) { + return Html::a( + // Используем встроенный SVG-код для иконки удаления + '', + ['delete', 'id' => $model->id], + [ + 'class' => 'btn btn-danger btn-sm', + 'title' => 'Редактировать', + 'data-bs-toggle' => 'tooltip', + 'data-bs-placement' => 'top', + 'data' => [ + 'confirm' => 'Вы уверены, что хотите удалить этот куст?', + 'method' => 'post', + ], + ] + ); }, ], ], @@ -74,6 +153,3 @@ $this->params['breadcrumbs'][] = $this->title;
- \ No newline at end of file diff --git a/erp24/views/cluster_link_edit/view-all.php b/erp24/views/cluster_link_edit/view-all.php index 707ddd34..05b74216 100644 --- a/erp24/views/cluster_link_edit/view-all.php +++ b/erp24/views/cluster_link_edit/view-all.php @@ -1,38 +1,157 @@ name) */ +/** @var string $clusterManager - Имя менеджера */ +/** @var string $currentDate - Текущая или выбранная дата */ $this->title = $model->name; $this->params['breadcrumbs'][] = ['label' => 'Clusters', 'url' => ['index']]; $this->params['breadcrumbs'][] = $this->title; -\yii\web\YiiAsset::register($this); + +// Получение списка магазинов, которые еще не находятся в текущем кусте +$storesInCluster = ArrayHelper::getColumn($dataProvider->getModels(), 'value_int'); +$availableStores = array_filter($storeNames, function ($storeId) use ($storesInCluster) { + return !in_array($storeId, $storesInCluster); +}, ARRAY_FILTER_USE_KEY); ?> -
- -

title) ?>

- -

- 'btn btn-primary']) ?> - $model->id], ['class' => 'btn btn-primary']) ?> - $model->id], [ - 'class' => 'btn btn-danger', - 'data' => [ - 'confirm' => 'Are you sure you want to delete this item?', - 'method' => 'post', + +

+ +

title) ?>

+ + +

Кустовой директор:

+ +

+ 'btn btn-primary']) ?> + + + + $model->id], 'post', ['class' => 'form-inline']) ?> + 'form-control', + 'prompt' => 'Выберите магазин', + 'id' => 'store-id-select' + ]) ?> + 'btn btn-secondary', + 'id' => 'add-store-button', + 'disabled' => 'disabled' + ]) ?> + + + +

+ + + $dataProvider, + 'columns' => [ + [ + 'attribute' => 'name', + 'label' => 'Наименование магазина', + 'value' => function ($store) use ($storeNames) { + return $storeNames[$store['value_int']] ?? 'Магазин не найден'; + }, + ], + [ + 'attribute' => 'date_from', + 'label' => 'Дата добавления в куст', + 'value' => function ($store) { + return $store['date_from']; + }, + ], + [ + 'attribute' => 'cluster_id', + 'label' => 'Перевести в другой куст', + 'format' => 'raw', + 'value' => function ($store) use ($clustersList, $model, $currentDate) { + if ($currentDate !== date('Y-m-d')) { + return Html::encode($model->name); // Показываем только имя куста, если дата не текущая + } + return Html::dropDownList('cluster_id', $store['cluster_id'], $clustersList, [ + 'class' => 'form-control', + 'data-store-id' => $store['id'], + 'options' => [ + $model->id => ['Selected' => true], // Текущий куст + ], + ]); + }, + ], + [ + 'class' => 'yii\grid\ActionColumn', + 'template' => '{delete}', + 'buttons' => [ + 'delete' => function ($url, $store) use ($model, $currentDate) { + if ($currentDate !== date('Y-m-d')) { + return ''; // Отключаем кнопку удаления, если дата не актуальна + } + return Html::a( + // Используем встроенный SVG-код для иконки удаления + '', + ['delete-store', 'id' => $store['id'], 'cluster_id' => $model->id], + [ + 'class' => 'btn btn-danger', + 'data' => [ + 'confirm' => 'Вы уверены, что хотите удалить этот магазин из куста?', + 'method' => 'post', + ], + ] + ); + }, + ], + ], ], - ]) ?> -

- - $model, - 'attributes' => [ - 'id', - 'name', - ], - ]) ?> -

Просмотр куста 1

-
+ ]); ?> +
+ +registerJs($js); +?> \ No newline at end of file -- 2.39.5