use yii_app\records\StoreVisitors;
use yii_app\records\WriteOffs;
use yii_app\records\WriteOffsErp;
+use yii_app\records\TimetableFactModel;
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);
$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');
$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;
$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'];
"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,
"total_potted_per_day" => $totalPottedPerDayTotal,
"employee_positions_on_shift" => $employeePositionsOnShift,
"employee_skills_score" => $totalEmployeeSkillsScore,
+ "employee_count_on_shift" => count($timetables),
];
// Создаем итоговый массив для дня с правильным порядком полей
$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);
// Сохраняем сотрудников для этого магазина
"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];
$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],
}
}
- $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);
$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);
// Сохраняем данные сотрудников для этого магазина