Files
sam-manage/app/Services/EquipmentInspectionService.php
김보곤 ba792a0fcc fix: [equipment] 점검표 휴일 표시 및 주간 1주 저장 버그 수정
- 점검 그리드에 holidays 테이블 기반 휴일 표시 (빨간 배경)
- 휴일/주말 셀 클릭 차단 (UI + 서버 양쪽)
- 자동 판정에서 휴일 제외 (기존 주말만 제외 → 주말+휴일)
- 주간 1주 열 저장 누락 수정 (resolvePeriod에서 isoWeekYear 사용)
- toggleDetail, setResult에 비근무일 검증 추가
- 범례에 '휴일/주말 (점검 불가)' 안내 추가
2026-02-28 15:30:11 +09:00

339 lines
11 KiB
PHP

<?php
namespace App\Services;
use App\Enums\InspectionCycle;
use App\Models\Equipment\Equipment;
use App\Models\Equipment\EquipmentInspection;
use App\Models\Equipment\EquipmentInspectionDetail;
use App\Models\Equipment\EquipmentInspectionTemplate;
class EquipmentInspectionService
{
/**
* 주기별 점검 데이터 조회
*
* @param string $cycle 점검주기 (daily/weekly/monthly/bimonthly/quarterly/semiannual)
* @param string $period 기간 (daily: 2026-02, 그 외: 2026)
*/
public function getInspections(string $cycle, string $period, ?string $productionLine = null, ?int $equipmentId = null): array
{
$equipmentQuery = Equipment::where('is_active', true)
->where('status', '!=', 'disposed')
->with(['manager', 'subManager']);
if ($productionLine) {
$equipmentQuery->where('production_line', $productionLine);
}
if ($equipmentId) {
$equipmentQuery->where('id', $equipmentId);
}
$equipments = $equipmentQuery->orderBy('sort_order')->orderBy('name')->get();
$labels = InspectionCycle::columnLabels($cycle, $period);
$result = [];
foreach ($equipments as $equipment) {
$templates = EquipmentInspectionTemplate::where('equipment_id', $equipment->id)
->where('inspection_cycle', $cycle)
->where('is_active', true)
->orderBy('sort_order')
->get();
if ($templates->isEmpty()) {
continue;
}
$inspection = EquipmentInspection::where('equipment_id', $equipment->id)
->where('inspection_cycle', $cycle)
->where('year_month', $period)
->first();
$details = [];
if ($inspection) {
$details = EquipmentInspectionDetail::where('inspection_id', $inspection->id)
->get()
->groupBy(function ($d) {
return $d->template_item_id.'_'.$d->check_date->format('Y-m-d');
});
}
$result[] = [
'equipment' => $equipment,
'templates' => $templates,
'inspection' => $inspection,
'details' => $details,
'labels' => $labels,
'can_inspect' => $equipment->canInspect(),
];
}
return $result;
}
/**
* 하위호환: 기존 getMonthlyInspections 유지
*/
public function getMonthlyInspections(string $yearMonth, ?string $productionLine = null, ?int $equipmentId = null): array
{
return $this->getInspections(InspectionCycle::DAILY, $yearMonth, $productionLine, $equipmentId);
}
/**
* 셀 토글 (점검 결과 순환)
*/
public function toggleDetail(int $equipmentId, int $templateItemId, string $checkDate, string $cycle = 'daily'): array
{
$equipment = Equipment::findOrFail($equipmentId);
if (! $equipment->canInspect()) {
throw new \Exception('점검 권한이 없습니다.');
}
$period = InspectionCycle::resolvePeriod($cycle, $checkDate);
$holidayDates = InspectionCycle::getHolidayDates($cycle, $period);
if (InspectionCycle::isNonWorkingDay($checkDate, $holidayDates)) {
throw new \Exception('휴일/주말에는 점검을 기록할 수 없습니다.');
}
$tenantId = session('selected_tenant_id', 1);
$inspection = EquipmentInspection::firstOrCreate(
[
'tenant_id' => $tenantId,
'equipment_id' => $equipmentId,
'inspection_cycle' => $cycle,
'year_month' => $period,
],
[
'created_by' => auth()->id(),
]
);
$detail = EquipmentInspectionDetail::where('inspection_id', $inspection->id)
->where('template_item_id', $templateItemId)
->where('check_date', $checkDate)
->first();
if ($detail) {
$nextResult = EquipmentInspectionDetail::getNextResult($detail->result);
if ($nextResult === null) {
$detail->delete();
return ['result' => null, 'symbol' => '', 'color' => 'text-gray-400'];
}
$detail->update(['result' => $nextResult]);
} else {
$detail = EquipmentInspectionDetail::create([
'inspection_id' => $inspection->id,
'template_item_id' => $templateItemId,
'check_date' => $checkDate,
'result' => 'good',
]);
$nextResult = 'good';
}
return [
'result' => $nextResult,
'symbol' => $detail->fresh()->result_symbol,
'color' => $detail->fresh()->result_color,
];
}
/**
* 점검 결과 직접 설정 (모바일용)
*/
public function setResult(int $equipmentId, int $templateItemId, string $checkDate, string $cycle, ?string $result): array
{
$equipment = Equipment::findOrFail($equipmentId);
if (! $equipment->canInspect()) {
throw new \Exception('점검 권한이 없습니다.');
}
$period = InspectionCycle::resolvePeriod($cycle, $checkDate);
$holidayDates = InspectionCycle::getHolidayDates($cycle, $period);
if (InspectionCycle::isNonWorkingDay($checkDate, $holidayDates)) {
throw new \Exception('휴일/주말에는 점검을 기록할 수 없습니다.');
}
$tenantId = session('selected_tenant_id', 1);
$inspection = EquipmentInspection::firstOrCreate(
[
'tenant_id' => $tenantId,
'equipment_id' => $equipmentId,
'inspection_cycle' => $cycle,
'year_month' => $period,
],
[
'created_by' => auth()->id(),
]
);
$detail = EquipmentInspectionDetail::where('inspection_id', $inspection->id)
->where('template_item_id', $templateItemId)
->where('check_date', $checkDate)
->first();
if ($result === null) {
if ($detail) {
$detail->delete();
}
return ['result' => null, 'symbol' => '', 'color' => 'text-gray-400'];
}
if ($detail) {
$detail->update(['result' => $result]);
} else {
$detail = EquipmentInspectionDetail::create([
'inspection_id' => $inspection->id,
'template_item_id' => $templateItemId,
'check_date' => $checkDate,
'result' => $result,
]);
}
return [
'result' => $result,
'symbol' => $detail->fresh()->result_symbol,
'color' => $detail->fresh()->result_color,
];
}
public function updateInspectionNotes(int $equipmentId, string $yearMonth, array $data, string $cycle = 'daily'): EquipmentInspection
{
$tenantId = session('selected_tenant_id', 1);
$inspection = EquipmentInspection::firstOrCreate(
[
'tenant_id' => $tenantId,
'equipment_id' => $equipmentId,
'inspection_cycle' => $cycle,
'year_month' => $yearMonth,
],
[
'created_by' => auth()->id(),
]
);
$inspection->update(array_merge($data, ['updated_by' => auth()->id()]));
return $inspection->fresh();
}
public function getMonthlyStats(string $yearMonth): array
{
$totalEquipments = Equipment::where('is_active', true)
->where('status', '!=', 'disposed')
->count();
$inspected = EquipmentInspection::where('year_month', $yearMonth)->count();
$issueCount = EquipmentInspectionDetail::whereHas('inspection', function ($q) use ($yearMonth) {
$q->where('year_month', $yearMonth);
})->where('result', 'bad')->count();
return [
'total' => $totalEquipments,
'inspected' => $inspected,
'issue_count' => $issueCount,
];
}
public function saveTemplate(int $equipmentId, array $data): EquipmentInspectionTemplate
{
$tenantId = session('selected_tenant_id', 1);
return EquipmentInspectionTemplate::create(array_merge($data, [
'tenant_id' => $tenantId,
'equipment_id' => $equipmentId,
]));
}
public function updateTemplate(int $id, array $data): EquipmentInspectionTemplate
{
$template = EquipmentInspectionTemplate::findOrFail($id);
$template->update($data);
return $template->fresh();
}
public function deleteTemplate(int $id): bool
{
return EquipmentInspectionTemplate::findOrFail($id)->delete();
}
/**
* 점검항목을 다른 주기로 복사
*/
public function copyTemplatesToCycles(int $equipmentId, string $sourceCycle, array $targetCycles): array
{
$tenantId = session('selected_tenant_id', 1);
$sourceTemplates = EquipmentInspectionTemplate::where('equipment_id', $equipmentId)
->where('inspection_cycle', $sourceCycle)
->where('is_active', true)
->orderBy('sort_order')
->get();
if ($sourceTemplates->isEmpty()) {
throw new \Exception('복사할 점검항목이 없습니다.');
}
$copiedCount = 0;
$skippedCount = 0;
foreach ($targetCycles as $targetCycle) {
foreach ($sourceTemplates as $template) {
$exists = EquipmentInspectionTemplate::where('equipment_id', $equipmentId)
->where('inspection_cycle', $targetCycle)
->where('item_no', $template->item_no)
->exists();
if ($exists) {
$skippedCount++;
continue;
}
EquipmentInspectionTemplate::create([
'tenant_id' => $tenantId,
'equipment_id' => $equipmentId,
'inspection_cycle' => $targetCycle,
'item_no' => $template->item_no,
'check_point' => $template->check_point,
'check_item' => $template->check_item,
'check_timing' => $template->check_timing,
'check_frequency' => $template->check_frequency,
'check_method' => $template->check_method,
'sort_order' => $template->sort_order,
'is_active' => true,
]);
$copiedCount++;
}
}
return [
'copied' => $copiedCount,
'skipped' => $skippedCount,
'source_count' => $sourceTemplates->count(),
'target_cycles' => $targetCycles,
];
}
/**
* 설비에 등록된 점검주기 목록 반환 (항목이 있는 주기만)
*/
public function getActiveCycles(int $equipmentId): array
{
return EquipmentInspectionTemplate::where('equipment_id', $equipmentId)
->where('is_active', true)
->distinct()
->pluck('inspection_cycle')
->toArray();
}
}