]> gitweb.erp-flowers.ru Git - erp24_rep/yii-erp24/.git/commitdiff
Вывод employee_count_on_shift в отчете
authorVladimir Fomichev <vladimir.fomichev@erp-flowers.ru>
Tue, 2 Dec 2025 14:18:22 +0000 (17:18 +0300)
committerVladimir Fomichev <vladimir.fomichev@erp-flowers.ru>
Tue, 2 Dec 2025 14:18:22 +0000 (17:18 +0300)
erp24/actions/grade/AdminUpdateAction.php
erp24/api3/core/services/ReportService.php
erp24/records/AdminGroup.php

index 968766fa6600cfc8ef4de22a3075eb752556767d..8a581d0b0dd5dfe4db1ba545bf444fdd6504c3fd 100644 (file)
@@ -97,6 +97,7 @@ class AdminUpdateAction extends Action
                     AdminGroup::GROUP_WORKERS,          // 45
                     AdminGroup::GROUP_ADMINISTRATORS,   // 50
                     AdminGroup::GROUP_FLORIST_SUPPORT_NIGHT, // 72
+                    AdminGroup::GROUP_FLORIST, // 89
                 ];
 
                 // Ищем группу "Работники магазинов" по имени
index 7e405a36a06c5b600735cf6d7c0fc0588ce5eee6..c0798580ff4497dc091a4734f333b69c279f5934 100644 (file)
@@ -20,6 +20,7 @@ use yii_app\records\StoreStaffing;
 use yii_app\records\StoreVisitors;
 use yii_app\records\WriteOffs;
 use yii_app\records\WriteOffsErp;
+use yii_app\records\TimetableFactModel;
 
 class ReportService
 {
@@ -321,27 +322,19 @@ class ReportService
         $totalEmployeePositionsOnShift = [];
         $totalEmployeeSkillsScores = [];
 
-        $employees = Sales::find()->select(["COUNT(*) as cnt", "admin_id"])
-            ->where([
-                'between',
-                'date',
-                date("Y-m-d 00:00:00", strtotime($data->date_start)),
-                date("Y-m-d 23:59:59", strtotime($data->date_end))])
-            ->andWhere(['store_id' => $data->stores])
-            ->groupBy(['admin_id'])->asArray()->all();
-        $employeeCount = count($employees);
-
-        // Получаем все уникальные admin_id сотрудников за период
-        $allAdminsInPeriod = Timetable::find()->alias('t')
+        // Получаем все уникальные admin_id сотрудников за период из фактических смен
+        $allAdminsInPeriod = TimetableFactModel::find()
             ->select(['admin_id'])
             ->distinct()
-            ->where(['t.store_id' => $data->stores])
+            ->where(['store_id' => $data->stores])
             ->andWhere(['>=', 'date', date("Y-m-01", strtotime($data->date_start))])
             ->andWhere(['<=', 'date', $data->date_end])
-            ->andWhere(['tabel' => 0, 'slot_type_id' => Timetable::TIMESLOT_WORK])
-            ->asArray()->all();
+            ->andWhere(['>', 'work_time', 0])
+            ->asArray()
+            ->all();
 
         $adminIdsInPeriod = ArrayHelper::getColumn($allAdminsInPeriod, 'admin_id');
+        $employeeCount = count($adminIdsInPeriod);
 
         $positionMap = $this->buildPositionMap($adminIdsInPeriod);
         $adminSkillMap = $this->buildAdminSkillMap($adminIdsInPeriod);
@@ -352,20 +345,90 @@ class ReportService
 
             $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])
-                ->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])
-                ->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($timetablesMonthData, 'admin_id');
+            $adminIds = 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'],
+                ];
+            }
 
             $adminIdsMonth = ArrayHelper::getColumn($timetablesMonth, 'admin_id');
             $adminIds = ArrayHelper::getColumn($timetables, 'admin_id');
@@ -610,6 +673,40 @@ class ReportService
                     $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;
+                }
+
+                // Добавляем админов из продаж, которых нет в смене
+                foreach ($salesAdminIds as $adminId => $_) {
+                    $adminExists = false;
+                    foreach ($adminNames[$store->id] as $admin) {
+                        if ($admin['id'] == $adminId) {
+                            $adminExists = true;
+                            break;
+                        }
+                    }
+                    if (!$adminExists) {
+                        // Получаем имя админа
+                        $adminName = Admin::findOne($adminId);
+                        $adminNames[$store->id][] = [
+                            'id' => $adminId,
+                            'name' => $adminName ? $adminName->name : 'Unknown',
+                            'shift_id' => 0, // У админов из продаж нет shift_id
+                        ];
+                    }
+                }
+
                 $storeVisitorsQuantityTotal += $storeVisitorsQuantity;
                 $storeSaleQuantityTotal += $storeSaleQuantity;
                 $storeSaleTotalTotal += $storeSaleTotal;
@@ -645,18 +742,34 @@ class ReportService
                 $totalPayrollDaysTotal += $totalPayrollDays;
                 $totalPayrollMonthTotal += $totalPayrollMonth;
 
+                // Инициализируем все необходимые поля для админов
+                if (isset($adminNames[$store->id])) {
+                    foreach ($adminNames[$store->id] as &$adminRecord) {
+                        // Инициализируем поля продаж, если они еще не установлены
+                        if (!isset($adminRecord["sale_total"])) {
+                            $adminRecord["sale_total"] = 0;
+                            $adminRecord["sale_quantity"] = 0;
+                            $adminRecord["sale_avg"] = 0;
+                            $adminRecord["sale_return_quantity"] = 0;
+                            $adminRecord["sale_return_total"] = 0;
+                            $adminRecord["bonus_user_count"] = 0;
+                            $adminRecord["bonus_user_per_sale_percent"] = 0;
+                            $adminRecord["bonus_new_user_count"] = 0;
+                            $adminRecord["bonus_repeat_user_count"] = 0;
+                        }
+                        // Инициализируем поля спецпродажи
+                        $adminRecord["total_matrix_per_day"] = 0;
+                        $adminRecord["total_matrix_per_day_percent"] = 0;
+                        $adminRecord["total_wrap_per_day"] = 0;
+                        $adminRecord["total_services_per_day"] = 0;
+                        $adminRecord["total_potted_per_day"] = 0;
+                    }
+                    unset($adminRecord);
+                }
+
                 $totalSpecificPerDay = [];
                 foreach (['matrix', 'wrap', 'services', 'potted'] as $spec) {
                     $totalSpecificPerDay[$spec] = 0;
-                    if (isset($adminNames[$store->id])) {
-                        foreach ($adminNames[$store->id] as &$adminRecord) {
-                            $adminRecord["total_" . $spec . "_per_day"] = 0;
-                            if ($spec == 'matrix') {
-                                $adminRecord["total_" . $spec . "_per_day_percent"] = 0;
-                            }
-                        }
-                        unset($adminRecord); // Освобождаем ссылку после цикла
-                    }
                     foreach ($specificSales[$spec] as $specificSale) {
                         if ($specificSale['store_id'] == $store->id) {
                             $totalSpecificPerDay[$spec] += $specificSale['total'];
@@ -728,6 +841,7 @@ class ReportService
                     "employee_positions_on_shift" => $storeEmployeePositionsOnShift,
                     "employee_skills_score" => $employeeSkillsScore,
                     "employee_skills_score_is_estimated" => $skillsScoreIsEstimated,
+                    "employee_count_on_shift" => isset($adminNames[$store->id]) ? count($adminNames[$store->id]) : 0,
                     "visitors_quantity" => $storeVisitorsQuantity,
                     "sale_quantity" => $storeSaleQuantity,
                     "sale_total" => $storeSaleTotal,
@@ -786,6 +900,7 @@ class ReportService
                 "total_potted_per_day" => $totalPottedPerDayTotal,
                 "employee_positions_on_shift" => $employeePositionsOnShift,
                 "employee_skills_score" => $totalEmployeeSkillsScore,
+                "employee_count_on_shift" => count($timetables),
             ];
 
             // Создаем итоговый массив для дня с правильным порядком полей
@@ -828,20 +943,25 @@ class ReportService
             $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);
@@ -852,13 +972,19 @@ class ReportService
             $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);
 
                 // Сохраняем сотрудников для этого магазина
@@ -1198,6 +1324,7 @@ class ReportService
                     "employee_positions_on_shift" => $storeEmployeePositionsOnShift,
                     "employee_skills_score" => $employeeSkillsScore,
                     "employee_skills_score_is_estimated" => $skillsScoreIsEstimated,
+                    "employee_count_on_shift" => $employeeCount[$store_id] ?? 0,
                 ];
                 $stores []= ['id' => $store_id, 'guid' => $eitStores[$store_id]['export_val'],
                     'name' => $cityStoreNames[$store_id], 'data' => $store];
@@ -1238,6 +1365,7 @@ class ReportService
             $total["conversion"] = $total["visitors_quantity"] > 0 ? floor($total["sale_quantity"] / $total["visitors_quantity"] * 100) : 0;;
             $total["bonus_user_per_sale_percent"] = $total["sale_quantity"] > 0 ? floor($total["bonus_user_count"] / $total["sale_quantity"] * 100) : 0;
             $total["employee_positions_on_shift"] = $employeePositionsOnShift;
+            $total["employee_count_on_shift"] = count($weekEmployees);
 
             $report = [
                 "date_from" => $dateStartEnd[0],
@@ -1289,24 +1417,19 @@ class ReportService
             }
         }
 
-        $cond = ['or'];
-        foreach ($days as $ind => $day) {
-            $cond[] = [
-                'between',
-                'date',
-                date("Y-m-d 00:00:00", strtotime($day)),
-                date("Y-m-d 23:59:59", strtotime($day))
-            ];
-        }
-        $employeesTotal = Sales::find()->select(["COUNT(*) as cnt", "admin_id"]
-        ) // , "DATA_FORMAT(date, '%Y-%m-%d') as day"
-        ->where(['store_id' => $data->stores])
-            ->andWhere($cond)
-            ->groupBy(['admin_id'])->asArray()->all();
-        $employeeCountTotal = count($employeesTotal);
+        // Получаем все уникальные admin_id сотрудников за период из фактических смен
+        $allAdminsInPeriod = TimetableFactModel::find()
+            ->select(['admin_id'])
+            ->distinct()
+            ->where(['store_id' => $data->stores])
+            ->andWhere(['>=', 'date', date("Y-m-01", strtotime($data->date))])
+            ->andWhere(['<=', 'date', date("Y-m-d", strtotime($data->date))])
+            ->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);
@@ -1316,16 +1439,18 @@ class ReportService
             $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);
 
                 // Сохраняем данные сотрудников для этого магазина
index f5f1a3cb984c788d248666f5a851201ca8893263..65c6512c41ac22ff4edf039b432ceb010e3a72fb 100755 (executable)
@@ -83,6 +83,23 @@ class AdminGroup extends ActiveRecord
         ];
     }
 
+    /**
+     * Возвращает список специальных групп работников (нужно для JS и логики грейдов)
+     * @return int[]
+     */
+    public static function getWorkersGroups(): array
+    {
+        return [
+            self::GROUP_FLORIST_DAY,      // 30
+            self::GROUP_FLORIST_NIGHT,    // 35
+            self::GROUP_FLORIST_SUPPORT_DAY, // 40
+            self::GROUP_WORKERS,          // 45
+            self::GROUP_ADMINISTRATORS,   // 50
+            self::GROUP_FLORIST_SUPPORT_NIGHT, // 72
+            self::GROUP_FLORIST,          // 89
+        ];
+    }
+
     public static function tableName()
     {
         return 'admin_group';