Доработал отчет для экономистов
This commit is contained in:
@@ -16,17 +16,29 @@ use Illuminate\Support\Facades\DB;
|
||||
|
||||
class StatisticsService
|
||||
{
|
||||
/**
|
||||
* Справочник типов оплаты (profit_type_id) — берётся из классификатора МИС (kl_ProfitType).
|
||||
*/
|
||||
public const PROFIT_TYPE_LABELS = [
|
||||
0 => 'Не определено',
|
||||
3 => 'ОМС',
|
||||
4 => 'Бюджет',
|
||||
5 => 'Платные услуги',
|
||||
6 => 'ДМС',
|
||||
7 => 'Другие',
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
protected BedDayService $bedDayService,
|
||||
protected PlanCalculator $planCalculator,
|
||||
) {}
|
||||
|
||||
public function getStatisticsData(User $user, string $startDate, string $endDate, bool $isRangeOneDay): array
|
||||
/**
|
||||
* Отделения, доступные пользователю, сгруппированные по типу отделения.
|
||||
*/
|
||||
private function resolveUserDepartments(User $user)
|
||||
{
|
||||
$this->bedDayService->clearMemoryCache();
|
||||
|
||||
// 1. Получаем отделения
|
||||
$departments = Department::select('department_id', 'name_short', 'rf_department_type', 'user_name', 'order')
|
||||
return Department::select('department_id', 'name_short', 'rf_department_type', 'user_name', 'order')
|
||||
->with('departmentType')
|
||||
->join((new UserDepartment)->getTable(), (new Department)->getTable().'.department_id', (new UserDepartment)->getTable().'.rf_department_id')
|
||||
->where((new UserDepartment)->getTable().'.rf_user_id', $user->id)
|
||||
@@ -34,6 +46,14 @@ class StatisticsService
|
||||
->orderBy((new UserDepartment)->getTable().'.order', 'asc')
|
||||
->get()
|
||||
->groupBy('departmentType.name_full');
|
||||
}
|
||||
|
||||
public function getStatisticsData(User $user, string $startDate, string $endDate, bool $isRangeOneDay): array
|
||||
{
|
||||
$this->bedDayService->clearMemoryCache();
|
||||
|
||||
// 1. Получаем отделения
|
||||
$departments = $this->resolveUserDepartments($user);
|
||||
|
||||
if ($departments->isEmpty()) {
|
||||
return $this->emptyResponse();
|
||||
@@ -289,6 +309,106 @@ class StatisticsService
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Количество поступлений по типу оплаты (profit_type_id), с разбивкой по отделениям.
|
||||
* "Поступление" определяется так же, как метрика "Поступило" на основном листе:
|
||||
* дата поступления миграции пациента попадает в период конкретного дежурного отчёта.
|
||||
*/
|
||||
public function getProfitTypeBreakdown(User $user, string $startDate, string $endDate): array
|
||||
{
|
||||
$departments = $this->resolveUserDepartments($user);
|
||||
|
||||
if ($departments->isEmpty()) {
|
||||
return $this->emptyProfitTypeResponse();
|
||||
}
|
||||
|
||||
$allDeptIds = $departments->flatten()->pluck('department_id')->toArray();
|
||||
|
||||
$rows = DB::table('report_duties as rd')
|
||||
->join('report_duty_patients as rdp', 'rdp.report_duty_id', '=', 'rd.id')
|
||||
->join('report_duty_migration_patients as rdmp', 'rdmp.medical_history_id', '=', 'rdp.id')
|
||||
->whereIn('rd.rf_department_id', $allDeptIds)
|
||||
->where('rd.period_start', '>=', $startDate)
|
||||
->where('rd.period_end', '<=', $endDate)
|
||||
->whereColumn('rdmp.ingoing_date', '>', 'rd.period_start')
|
||||
->whereColumn('rdmp.ingoing_date', '<=', 'rd.period_end')
|
||||
->select('rd.rf_department_id', 'rdp.profit_type_id', DB::raw('COUNT(DISTINCT rdp.id) as count'))
|
||||
->groupBy('rd.rf_department_id', 'rdp.profit_type_id')
|
||||
->get();
|
||||
|
||||
// Поддерживаем коды, которых пока нет в справочнике (на случай новых типов из МИС)
|
||||
$profitTypeLabels = self::PROFIT_TYPE_LABELS;
|
||||
foreach ($rows->pluck('profit_type_id')->unique() as $profitTypeId) {
|
||||
$profitTypeId = (int) $profitTypeId;
|
||||
if (! array_key_exists($profitTypeId, $profitTypeLabels)) {
|
||||
$profitTypeLabels[$profitTypeId] = "Тип оплаты #{$profitTypeId}";
|
||||
}
|
||||
}
|
||||
|
||||
$countsByDept = $rows->groupBy('rf_department_id');
|
||||
$emptyCounts = array_fill_keys(array_keys($profitTypeLabels), 0);
|
||||
|
||||
$data = [];
|
||||
$grandTotals = $emptyCounts;
|
||||
|
||||
foreach ($departments as $typeName => $deptList) {
|
||||
$groupTotals = $emptyCounts;
|
||||
$groupRows = [];
|
||||
|
||||
foreach ($deptList as $dept) {
|
||||
$deptId = $dept->department_id;
|
||||
$counts = $emptyCounts;
|
||||
|
||||
foreach ($countsByDept->get($deptId, collect()) as $row) {
|
||||
$counts[(int) $row->profit_type_id] = (int) $row->count;
|
||||
}
|
||||
|
||||
$groupRows[] = [
|
||||
'department' => $dept->user_name ?? $dept->name_short,
|
||||
'counts' => $counts,
|
||||
'total' => array_sum($counts),
|
||||
];
|
||||
|
||||
foreach ($counts as $profitTypeId => $count) {
|
||||
$groupTotals[$profitTypeId] += $count;
|
||||
$grandTotals[$profitTypeId] += $count;
|
||||
}
|
||||
}
|
||||
|
||||
$data[] = ['isGroupHeader' => true, 'groupName' => $typeName];
|
||||
array_push($data, ...$groupRows);
|
||||
$data[] = [
|
||||
'isTotalRow' => true,
|
||||
'department' => 'ИТОГО:',
|
||||
'counts' => $groupTotals,
|
||||
'total' => array_sum($groupTotals),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'data' => $data,
|
||||
'grandTotals' => [
|
||||
'department' => 'ОБЩИЕ ИТОГИ:',
|
||||
'counts' => $grandTotals,
|
||||
'total' => array_sum($grandTotals),
|
||||
],
|
||||
'profitTypes' => $profitTypeLabels,
|
||||
];
|
||||
}
|
||||
|
||||
private function emptyProfitTypeResponse(): array
|
||||
{
|
||||
return [
|
||||
'data' => [],
|
||||
'grandTotals' => [
|
||||
'department' => 'ОБЩИЕ ИТОГИ:',
|
||||
'counts' => array_fill_keys(array_keys(self::PROFIT_TYPE_LABELS), 0),
|
||||
'total' => 0,
|
||||
],
|
||||
'profitTypes' => self::PROFIT_TYPE_LABELS,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Посуточные ряды по каждому KPI за период — для фоновых спарклайнов.
|
||||
* Источник — суточные дежурные отчёты (report_duties + duty_report_metric_results).
|
||||
|
||||
Reference in New Issue
Block a user