]> gitweb.erp-flowers.ru Git - erp24_rep/yii-erp24/.git/commitdiff
Merge branch 'refs/heads/develop' into feature_fomichev_erp-497_add_count_on_shift_to...
authorVladimir Fomichev <vladimir.fomichev@erp-flowers.ru>
Thu, 4 Dec 2025 12:07:33 +0000 (15:07 +0300)
committerVladimir Fomichev <vladimir.fomichev@erp-flowers.ru>
Thu, 4 Dec 2025 12:07:33 +0000 (15:07 +0300)
# Conflicts:
# erp24/actions/grade/AdminUpdateAction.php
# erp24/api3/core/services/ReportService.php

1  2 
erp24/actions/grade/AdminUpdateAction.php
erp24/api3/core/services/ReportService.php
erp24/records/AdminGroup.php

index 8a581d0b0dd5dfe4db1ba545bf444fdd6504c3fd,70ae6133c1a3fc4052684670f567c5ae868f50ca..ada50c09129689a28752093685e8a1de5184b5c5
@@@ -31,6 -32,10 +32,10 @@@ class AdminUpdateAction extends Actio
              return "Нет доступа";
          }
          $model = Admin::findOne($id);
 -        
++
+         // Определяем специальные группы работников (используется в POST обработке и при рендеринге)
+         $workersGroup = AdminGroup::getWorkersGroups();
 -        
++
          if (Yii::$app->user->can("updateAdminSettings", ['id' => $model->id])) {
              if (Yii::$app->request->isPost) {
                  $attributes = Yii::$app->request->post()['Admin'];
                  unset($attributes['storeArray']);
                  $attributes['store_arr_guid'] = empty($attributes['storeGuidArray']) ? '' : implode(',', $attributes['storeGuidArray']);
                  unset($attributes['storeGuidArray']);
-                 if (!Yii::$app->user->can("updateAdminSettingsGroupId", ['group_id' => $attributes['group_id']])) {
-                     unset($attributes['group_id']);
 -                
++
+                 // Проверяем права на изменение group_id
+                 // Если group_id не передан в POST, используем текущий из модели
+                 $newGroupId = $attributes['group_id'] ?? $model->group_id;
 -                
++
+                 // Проверяем права только если group_id действительно меняется
+                 if ($newGroupId != $model->group_id) {
+                     $canChangeGroupId = Yii::$app->user->can("updateAdminSettingsGroupId", ['group_id' => $newGroupId]);
 -                    
++
+                     // Если нет прав на изменение group_id - используем текущий group_id из модели
+                     if (!$canChangeGroupId) {
+                         $attributes['group_id'] = $model->group_id;
+                     }
                  }
 -                
                  if (!Yii::$app->user->can("updateAdminSettingsOnlyByHrAndAdministrator")) {
                      unset($attributes['store_dostup_all']);
                      unset($attributes['store_id']);
                      Yii::$app->cache->set("dirtyAuthSettings", true);
                  }
  
-                 // Определяем специальные группы с parent_id = 50
-                 $specialGroups = [
-                     AdminGroup::GROUP_FLORIST_DAY,      // 30
-                     AdminGroup::GROUP_FLORIST_NIGHT,    // 35
-                     AdminGroup::GROUP_FLORIST_SUPPORT_DAY, // 40
-                     AdminGroup::GROUP_WORKERS,          // 45
-                     AdminGroup::GROUP_ADMINISTRATORS,   // 50
-                     AdminGroup::GROUP_FLORIST_SUPPORT_NIGHT, // 72
-                     AdminGroup::GROUP_FLORIST, // 89
-                 ];
-                 // Ищем группу "Работники магазинов" по имени
-                 $workersGroup = AdminGroup::find()->where(['name' => AdminGroup::GROUP_STORE_WORKERS_NAME])->one();
-                 if ($workersGroup) {
-                     $specialGroups[] = $workersGroup->id;
-                 }
+                 // Получаем группу один раз для всех последующих операций
+                 $currentGroup = AdminGroup::findOne($attributes['group_id']);
+                 $isSpecialGroup = in_array((int)$attributes['group_id'], $workersGroup);
 -                
 +
-                 $isSpecialGroup = in_array((int)$attributes['group_id'], $specialGroups);
-                 if ($isSpecialGroup) {
-                     // Для специальных групп формируем group_name из employee_position + shift
-                     if (!empty($attributes['employee_position_id'])) {
-                         $employeePosition = EmployeePosition::findOne($attributes['employee_position_id']);
-                         if ($employeePosition) {
-                             $groupName = $employeePosition->name;
-                             // Если выбрана смена, добавляем её к названию
-                             if (!empty($attributes['shift'])) {
-                                 $groupName .= ' ' . $attributes['shift'];
-                             }
-                             $attributes['group_name'] = $groupName;
-                         }
-                     }
+                 // Устанавливаем group_name из AdminGroup->name
+                 if ($currentGroup) {
+                     $attributes['group_name'] = $currentGroup->name;
+                 } else if (isset($attributes['custom_position'])) {
+                     // Если группа не найдена, используем текстовое поле как fallback
+                     $attributes['group_name'] = $attributes['custom_position'];
+                 }
 -                
 +
-                     // Очищаем shift для не-специальных групп
-                     unset($attributes['shift']);
-                 } else {
-                     // Для остальных групп group_name берем из текстового поля
-                     if (isset($attributes['custom_position'])) {
-                         $attributes['group_name'] = $attributes['custom_position'];
-                         unset($attributes['custom_position']);
-                     }
-                     // Очищаем employee_position_id и shift для не-специальных групп
+                 // Для не-специальных групп очищаем employee_position_id
+                 if (!$isSpecialGroup) {
                      $attributes['employee_position_id'] = null;
-                     unset($attributes['shift']);
                  }
 -                
 +
+                 // Очищаем custom_position в любом случае
+                 unset($attributes['custom_position']);
                  $model->setAttributes($attributes, false);
  
                  if (Yii::$app->user->can("manageAvatarka", ['id' => $model->id])) {
              }
          }
  
-         $adminGroups = [AdminGroup::NOT_INITIALIZED_GROUP => 'Не выбрана'];
-         foreach (AdminGroup::find()->all() as $adminGroupId => $adminGroup) {
-             $adminGroups[$adminGroup->id] = $adminGroup->name;
-         }
 -        $adminGroups = [AdminGroup::NOT_INITIALIZED_GROUP => 'Не выбрана'] + 
++        $adminGroups = [AdminGroup::NOT_INITIALIZED_GROUP => 'Не выбрана'] +
+             ArrayHelper::map(AdminGroup::find()->all(), 'id', 'name');
  
-         $adminArr = [];
-         foreach (Admin::find()->with('adminGroup')->all() as $admin) {
-             $adminArr[] = ['id' => $admin->id, 'name' => $admin->name, 'groupName' => $admin->adminGroup->name ?? "Другие"];
-         }
+         $admins = ArrayHelper::map(
 -            Admin::find()->with('adminGroup')->all(), 
 -            'id', 
 -            'name', 
++            Admin::find()->with('adminGroup')->all(),
++            'id',
++            'name',
+             function($model) {
+                 return $model->adminGroup->name ?? 'Другие';
+             }
+         );
  
-         $admins = ArrayHelper::map($adminArr, 'id', 'name', 'groupName');
          $positions = EmployeePosition::find()->orderBy('posit')->all();
          $cityStores = ArrayHelper::map(CityStore::find()->select(['id', 'name'])->all(), 'id', 'name');
  
index eb87a4c19f025408fc798a535d35b0447def1280,7d183959f2a2b1b2ffc606e95f4fd998f171bd6a..90e8a5c7b9cb05aa299e3d1d4d55da01197cb541
@@@ -345,91 -353,25 +345,91 @@@ class ReportServic
  
              $shift_id = $data->shift_type == 0 ? [1, 2, 5, 8] : ($data->shift_type == 1 ? [1, 5, 8] : [2]);
  
 -            $timetablesMonth = Timetable::find()->alias('t')->select(['admin_id', 'a.name as adminName', 't.store_id', 't.shift_id'])
 -                ->innerJoin('admin a', 'admin_id = a.id')
 -                ->where(['t.store_id' => $data->stores])
 +            // Получаем сотрудников из фактических смен за месяц
 +            $timetablesMonthData = TimetableFactModel::find()
 +                ->select(['admin_id', 'store_id', 'shift_id'])
 +                ->where(['store_id' => $data->stores])
                  ->andWhere(['>=', 'date', date("Y-m-01", strtotime($currentDate))])
                  ->andWhere(['<=', 'date', $currentDate])
 -                ->andWhere(['shift_id' => $shift_id, 'tabel' => 0, 'slot_type_id' => Timetable::TIMESLOT_WORK])
 -                ->andWhere(['t.active' => 1])
 -                ->asArray()->all();
 +                ->andWhere(['shift_id' => $shift_id])
 +                ->andWhere(['>', 'work_time', 0])
 +                ->groupBy(['admin_id', 'store_id', 'shift_id'])
 +                ->asArray()
 +                ->all();
 +
 +            // Если в факте нет данных за месяц, используем план как fallback
 +            if (empty($timetablesMonthData)) {
 +                $timetablesMonthData = Timetable::find()->alias('t')
 +                    ->select(['admin_id', 'store_id', 'shift_id'])
 +                    ->where(['t.store_id' => $data->stores])
 +                    ->andWhere(['>=', 't.date', date("Y-m-01", strtotime($currentDate))])
 +                    ->andWhere(['<=', 't.date', $currentDate])
 +                    ->andWhere(['t.shift_id' => $shift_id, 'tabel' => 0, 'slot_type_id' => Timetable::TIMESLOT_WORK])
 +                    ->groupBy(['admin_id', 'store_id', 'shift_id'])
 +                    ->asArray()
 +                    ->all();
 +            }
  
 -            $timetables = Timetable::find()->alias('t')->select(['admin_id', 'a.name as adminName', 't.store_id', 't.shift_id'])
 -                ->innerJoin('admin a', 'admin_id = a.id')
 -                ->where(['t.store_id' => $data->stores])
 -                ->andWhere(['date' => $currentDate, 'tabel' => 0])
 -                ->andWhere(['shift_id' => $shift_id, 'slot_type_id' => Timetable::TIMESLOT_WORK])
 -                ->andWhere(['t.active' => 1])
 -                ->asArray()->all();
 +            // Получаем сотрудников из фактических смен за день
 +            $timetablesData = TimetableFactModel::find()
 +                ->select(['admin_id', 'store_id', 'shift_id'])
 +                ->where(['store_id' => $data->stores])
 +                ->andWhere(['date' => $currentDate])
 +                ->andWhere(['shift_id' => $shift_id])
 +                ->andWhere(['>', 'work_time', 0])
 +                ->groupBy(['admin_id', 'store_id', 'shift_id'])
 +                ->asArray()
 +                ->all();
 +
 +            // Если в факте нет данных, используем план как fallback
 +            if (empty($timetablesData)) {
 +                $timetablesData = Timetable::find()->alias('t')
 +                    ->select(['admin_id', 'store_id', 'shift_id'])
 +                    ->where(['t.store_id' => $data->stores])
 +                    ->andWhere(['t.date' => $currentDate, 'tabel' => 0])
 +                    ->andWhere(['t.shift_id' => $shift_id, 'slot_type_id' => Timetable::TIMESLOT_WORK])
 +                    ->groupBy(['admin_id', 'store_id', 'shift_id'])
 +                    ->asArray()
 +                    ->all();
 +            }
  
 -            $adminIdsMonth = ArrayHelper::getColumn($timetablesMonth, 'admin_id');
 -            $adminIds = ArrayHelper::getColumn($timetables, 'admin_id');
 +            // Получаем имена админов отдельным запросом
 +            // Извлекаем уникальные ID администраторов для использования в последующих запросах
 +            $adminIdsMonth = array_unique(ArrayHelper::getColumn($timetablesMonthData, 'admin_id'));
 +            $adminIds = array_unique(ArrayHelper::getColumn($timetablesData, 'admin_id'));
 +            $allAdminIds = array_unique(array_merge($adminIdsMonth, $adminIds));
-             
++
 +            $adminNamesMap = [];
 +            if (!empty($allAdminIds)) {
 +                $admins = Admin::find()
 +                    ->select(['id', 'name'])
 +                    ->where(['id' => $allAdminIds])
 +                    ->indexBy('id')
 +                    ->asArray()
 +                    ->all();
 +                $adminNamesMap = ArrayHelper::map($admins, 'id', 'name');
 +            }
 +
 +            // Формируем массивы с именами админов
 +            $timetablesMonth = [];
 +            foreach ($timetablesMonthData as $item) {
 +                $timetablesMonth[] = [
 +                    'admin_id' => $item['admin_id'],
 +                    'adminName' => $adminNamesMap[$item['admin_id']] ?? '',
 +                    'store_id' => $item['store_id'],
 +                    'shift_id' => $item['shift_id'],
 +                ];
 +            }
 +
 +            $timetables = [];
 +            foreach ($timetablesData as $item) {
 +                $timetables[] = [
 +                    'admin_id' => $item['admin_id'],
 +                    'adminName' => $adminNamesMap[$item['admin_id']] ?? '',
 +                    'store_id' => $item['store_id'],
 +                    'shift_id' => $item['shift_id'],
 +                ];
 +            }
  
              // Подсчет должностей на смене для этого дня
              $employeePositionsOnShift = $this->countEmployeesByPosition($timetables, $positionMap);
  
              $adminNames = [];
              foreach ($timetables as $timetable) {
-                 $adminNames[$timetable['store_id']][] = [
+                 $storeId = $timetable['store_id'];
 -                
++
+                 if (!isset($adminNames[$storeId])) {
+                     $adminNames[$storeId] = [];
+                 }
+                  // Один администратор может работать в несколько смен в один день
+                 $adminNames[$storeId][] = [
                      'id' => $timetable['admin_id'],
                      'name' => $timetable['adminName'],
                      'shift_id' => $timetable['shift_id'],
                      $storeSaleReturnTotal += (int)$storeSale['total'];
                  }
  
-                 
 +                // Добавляем админов из продаж, которые отсутствуют в $adminNames
 +                if (!isset($adminNames[$store->id])) {
 +                    $adminNames[$store->id] = [];
 +                }
-                 
++
 +                // Собираем уникальные admin_id из продаж
 +                $salesAdminIds = [];
 +                foreach ($storeSaleArr as $sale) {
 +                    $salesAdminIds[$sale['admin_id']] = true;
 +                }
 +                foreach ($storeSaleReturnArr as $sale) {
 +                    $salesAdminIds[$sale['admin_id']] = true;
 +                }
 +
 +                // Определяем админов из продаж, которых нет в смене
 +                $missingAdminIds = [];
 +                foreach ($salesAdminIds as $adminId => $_) {
 +                    $adminExists = false;
 +                    foreach ($adminNames[$store->id] as $admin) {
 +                        if ($admin['id'] == $adminId) {
 +                            $adminExists = true;
 +                            break;
 +                        }
 +                    }
 +                    if (!$adminExists) {
 +                        $missingAdminIds[] = $adminId;
 +                    }
 +                }
-                     
++
 +                // Batch-загрузка имен отсутствующих админов одним запросом
 +                if (!empty($missingAdminIds)) {
 +                    $missingAdmins = Admin::find()
 +                        ->select(['id', 'name'])
 +                        ->where(['id' => $missingAdminIds])
 +                        ->indexBy('id')
 +                        ->asArray()
 +                        ->all();
++
 +                    foreach ($missingAdminIds as $adminId) {
 +                        $adminNames[$store->id][] = [
 +                            'id' => $adminId,
 +                            'name' => $missingAdmins[$adminId]['name'] ?? 'Unknown',
 +                            'shift_id' => 0, // У админов из продаж нет shift_id
 +                        ];
 +                    }
 +                }
 +
                  $storeVisitorsQuantityTotal += $storeVisitorsQuantity;
                  $storeSaleQuantityTotal += $storeSaleQuantity;
                  $storeSaleTotalTotal += $storeSaleTotal;
              $store_guids[$store_id] = $eitStores[$store_id]['export_val'];
          }
  
 +        // Получаем все уникальные admin_id сотрудников за период из фактических смен
          $cond = ['or'];
          foreach ($data->date as $ind => $dateStartEnd) {
 -            $cond[]= ['between', 'date',
 -                date("Y-m-d 00:00:00", strtotime($dateStartEnd[0])),
 -                date("Y-m-d 23:59:59", strtotime($dateStartEnd[1]))];
 +            $cond[] = ['between', 'date',
 +                date("Y-m-d", strtotime($dateStartEnd[0])),
 +                date("Y-m-d", strtotime($dateStartEnd[1]))];
          }
-         
 -        $employeesTotal = Sales::find()->select(["COUNT(*) as cnt", "admin_id"]) // , "DATA_FORMAT(date, '%Y-%m-%d') as day"
++
 +        $allAdminsInPeriod = TimetableFactModel::find()
 +            ->select(['admin_id'])
 +            ->distinct()
              ->where(['store_id' => $data->stores])
              ->andWhere($cond)
 -            ->groupBy(['admin_id'])->asArray()->all();
 -        $employeeCountTotal = count($employeesTotal);
 +            ->andWhere(['>', 'work_time', 0])
 +            ->asArray()
 +            ->all();
  
 -        // Получаем все уникальные admin_id сотрудников за период
 -        $adminIdsInPeriod = ArrayHelper::getColumn($employeesTotal, 'admin_id');
 +        $adminIdsInPeriod = ArrayHelper::getColumn($allAdminsInPeriod, 'admin_id');
 +        $employeeCountTotal = count($adminIdsInPeriod);
  
          $positionMap = $this->buildPositionMap($adminIdsInPeriod);
          $adminSkillMap = $this->buildAdminSkillMap($adminIdsInPeriod);
              $employeeCount = [];
              $weekEmployees = []; // Собираем сотрудников за неделю
              $storeWeekEmployees = []; // Сотрудники по магазинам за неделю
-             
++
 +            // Получаем сотрудников из фактических смен за неделю
              foreach ($data->stores as $store_id) {
 -                $employees = Sales::find()->select(["COUNT(*) as cnt", "admin_id"])
 -                    ->where(['between', 'date',
 -                        date("Y-m-d 00:00:00", strtotime($dateStartEnd[0])),
 -                        date("Y-m-d 23:59:59", strtotime($dateStartEnd[1]))])
 -                    ->andWhere(['store_id' => $store_id])
 -                    ->groupBy(['admin_id'])->asArray()->all();
 +                $employees = TimetableFactModel::find()
 +                    ->select(['admin_id'])
 +                    ->distinct()
 +                    ->where(['store_id' => $store_id])
 +                    ->andWhere(['>=', 'date', date("Y-m-d", strtotime($dateStartEnd[0]))])
 +                    ->andWhere(['<=', 'date', date("Y-m-d", strtotime($dateStartEnd[1]))])
 +                    ->andWhere(['>', 'work_time', 0])
 +                    ->asArray()
 +                    ->all();
-                 
++
                  $employeeCount[$store_id] = count($employees);
  
                  // Сохраняем сотрудников для этого магазина
              $employeeCount = [];
              $allDayEmployees = []; // Собираем всех сотрудников за день по всем магазинам
              $storeEmployeesData = []; // Данные о сотрудниках по магазинам
-             
++
 +            // Получаем сотрудников из фактических смен за день
              foreach ($data->stores as $store_id) {
 -                $employees = Sales::find()->select(["COUNT(*) as cnt", "admin_id"])
 -                    ->where([
 -                        'between',
 -                        'date',
 -                        date("Y-m-d 00:00:00", strtotime($day)),
 -                        date("Y-m-d 23:59:59", strtotime($day))
 -                    ])
 -                    ->andWhere(['store_id' => $store_id])
 -                    ->groupBy(['admin_id'])->asArray()->all();
 +                $employees = TimetableFactModel::find()
 +                    ->select(['admin_id'])
 +                    ->distinct()
 +                    ->where(['store_id' => $store_id])
 +                    ->andWhere(['date' => $day])
 +                    ->andWhere(['>', 'work_time', 0])
 +                    ->asArray()
 +                    ->all();
-                 
++
                  $employeeCount[$store_id] = count($employees);
  
                  // Сохраняем данные сотрудников для этого магазина
Simple merge