- 점검 그리드에 holidays 테이블 기반 휴일 표시 (빨간 배경) - 휴일/주말 셀 클릭 차단 (UI + 서버 양쪽) - 자동 판정에서 휴일 제외 (기존 주말만 제외 → 주말+휴일) - 주간 1주 열 저장 누락 수정 (resolvePeriod에서 isoWeekYear 사용) - toggleDetail, setResult에 비근무일 검증 추가 - 범례에 '휴일/주말 (점검 불가)' 안내 추가
339 lines
11 KiB
PHP
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();
|
|
}
|
|
}
|