Files
onboard/app/Infrastructure/Reports/Services/ReportMetadataReadService.php

169 lines
6.1 KiB
PHP

<?php
namespace App\Infrastructure\Reports\Services;
use App\Domain\Reports\ValueObjects\MetrikaConfig;
use App\Models\Department;
use App\Models\MisLpuDoctor;
use App\Models\Report;
use App\Models\UnwantedEvent;
use App\Models\User;
use App\Services\DateRange;
use Carbon\Carbon;
use Illuminate\Support\Collection;
/**
* Read-side сервис для metadata отчёта: текущий отчёт, события, планы и периоды.
*/
class ReportMetadataReadService
{
public function __construct(
private readonly ReportReadContextResolver $contextResolver,
) {}
public function getCurrentReportInfo(Department $department, User $user, DateRange $dateRange): array
{
$reportToday = $this->contextResolver->resolveReportForPeriod($department->department_id, $dateRange);
$isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin();
$useSnapshots = $isHeadOrAdmin || ! $dateRange->isEndDateToday() || $reportToday;
if ($useSnapshots && $isHeadOrAdmin && $reportToday) {
$fillableUserId = $reportToday->rf_lpudoctor_id ?? null;
} else {
$fillableUserId = request()->query('userId', $user->rf_lpudoctor_id);
}
$unwantedEvents = $this->getUnwantedEvents($department, $dateRange);
$isActiveSendButton = $this->isSendButtonActive($user, $dateRange, $reportToday);
$message = null;
if ($reportToday) {
$reportDoctor = $reportToday->lpuDoctor;
$message = "Отчет создан пользователем: $reportDoctor->FAM_V $reportDoctor->IM_V $reportDoctor->OT_V";
}
$statusMessage = $reportToday
? ($reportToday->status === 'submitted'
? 'Этот отчет в статусе: опубликован'
: 'Этот отчет в статусе: черновик')
: null;
$lpuDoctor = $this->getDoctorInfo($fillableUserId, $dateRange);
$date = $isHeadOrAdmin ? [
$dateRange->startDate->getTimestampMs(),
$dateRange->endDate->getTimestampMs(),
] : $dateRange->endDate->getTimestampMs();
return [
'report_id' => $reportToday?->report_id,
'unwantedEvents' => $unwantedEvents,
'isActiveSendButton' => $isActiveSendButton,
'message' => $dateRange->isOneDay ? $message : null,
'status' => $reportToday?->status ?? 'draft',
'statusMessage' => $dateRange->isOneDay ? $statusMessage : null,
'canPublish' => (bool) $reportToday && ($reportToday->status === 'draft') && $isActiveSendButton,
'isOneDay' => $dateRange->isOneDay,
'isHeadOrAdmin' => $isHeadOrAdmin,
'dates' => $date,
'userId' => $fillableUserId,
'userName' => $lpuDoctor ? "$lpuDoctor->FAM_V $lpuDoctor->IM_V $lpuDoctor->OT_V" : null,
];
}
/**
* @return Collection<int, array<string, mixed>>
*/
public function getUnwantedEvents(Department $department, DateRange $dateRange): Collection
{
return UnwantedEvent::query()
->whereHas('report', function ($query) use ($department, $dateRange) {
$query->where('rf_department_id', $department->department_id);
if ($dateRange->isOneDay) {
$query->exactPeriod($dateRange->startSql(), $dateRange->endSql());
} else {
$query->withinPeriod($dateRange->startSql(), $dateRange->endSql());
}
})
->get()
->map(function (UnwantedEvent $item) {
return [
...$item->toArray(),
'created_at' => Carbon::parse($item->created_at)->format('Создано d.m.Y в H:i'),
];
});
}
/**
* @return Collection<int, Report>
*/
public function getReportsForDateRange(int $departmentId, DateRange $dateRange): Collection
{
return $this->contextResolver->getReportsForDateRange($departmentId, $dateRange);
}
public function getRecipientPlanOfYear(Department $department, DateRange $dateRange): array
{
$periodPlanModel = $department->recipientPlanOfYear();
$monthsInPeriod = ceil($dateRange->startDate->diffInMonths($dateRange->endDate));
$annualPlan = $periodPlanModel ? (int) $periodPlanModel->value : 0;
$oneMonthPlan = ceil($annualPlan / 12);
$periodPlan = round($oneMonthPlan * $monthsInPeriod);
$query = $department->reports()
->with('metrikaResults')
->where('period_start', '>', $dateRange->startSql())
->where('period_end', '<=', $dateRange->endSql());
if ($dateRange->isOneDay) {
$query->where('period_start', '>=', $dateRange->startFirstOfMonth())
->where('period_end', '<=', $dateRange->endSql());
} else {
$query->where('period_start', '>', $dateRange->startSql())
->where('period_end', '<=', $dateRange->endSql());
}
$progress = 0;
foreach ($query->get() as $report) {
$outcome = $report->metrikaResults()
->where('rf_metrika_item_id', MetrikaConfig::OUTCOME)
->first();
if ($outcome) {
$progress += (int) $outcome->value;
}
}
return [
'plan' => $periodPlan,
'progress' => $progress,
];
}
private function isSendButtonActive(User $user, DateRange $dateRange, ?Report $reportToday): bool
{
if (! $user->isHeadOfDepartment() && ! $user->isAdmin()) {
if ($reportToday && $reportToday->status === 'submitted') {
return false;
}
return $dateRange->isEndDateToday();
}
return (bool) $reportToday && $dateRange->isOneDay;
}
private function getDoctorInfo(?int $doctorId, DateRange $dateRange): ?MisLpuDoctor
{
if (! $doctorId || ! $dateRange->isOneDay) {
return null;
}
return MisLpuDoctor::query()
->where('LPUDoctorID', $doctorId)
->first();
}
}