format('Y-m-d'); // 작업지시 (work_orders) $woCreated = DB::connection('mysql') ->table('work_orders') ->where('tenant_id', $tenantId) ->whereDate('created_at', $dateStr) ->whereNull('deleted_at') ->count(); $woCompleted = DB::connection('mysql') ->table('work_orders') ->where('tenant_id', $tenantId) ->whereDate('completed_at', $dateStr) ->whereNull('deleted_at') ->count(); $woInProgress = DB::connection('mysql') ->table('work_orders') ->where('tenant_id', $tenantId) ->where('status', 'in_progress') ->whereNull('deleted_at') ->count(); // 납기 초과 (scheduled_date < today && status not completed/shipped) $woOverdue = DB::connection('mysql') ->table('work_orders') ->where('tenant_id', $tenantId) ->where('scheduled_date', '<', $dateStr) ->whereNotIn('status', ['completed', 'shipped', 'cancelled']) ->whereNull('deleted_at') ->count(); // 생산 실적 (work_results) $productionStats = DB::connection('mysql') ->table('work_results') ->where('tenant_id', $tenantId) ->where('work_date', $dateStr) ->whereNull('deleted_at') ->selectRaw(' COALESCE(SUM(production_qty), 0) as production_qty, COALESCE(SUM(defect_qty), 0) as defect_qty, COUNT(DISTINCT worker_id) as active_worker_count ') ->first(); $productionQty = $productionStats->production_qty ?? 0; $defectQty = $productionStats->defect_qty ?? 0; $defectRate = $productionQty > 0 ? ($defectQty / $productionQty) * 100 : 0; // 납기 준수 (당일 완료된 작업지시 중 scheduled_date >= completed_at인 것) $onTimeCount = DB::connection('mysql') ->table('work_orders') ->where('tenant_id', $tenantId) ->whereDate('completed_at', $dateStr) ->whereNull('deleted_at') ->whereRaw('DATE(completed_at) <= scheduled_date') ->count(); $lateCount = $woCompleted - $onTimeCount; $deliveryRate = $woCompleted > 0 ? ($onTimeCount / $woCompleted) * 100 : 0; StatProductionDaily::updateOrCreate( ['tenant_id' => $tenantId, 'stat_date' => $dateStr], [ 'wo_created_count' => $woCreated, 'wo_completed_count' => $woCompleted, 'wo_in_progress_count' => $woInProgress, 'wo_overdue_count' => $woOverdue, 'production_qty' => $productionQty, 'defect_qty' => $defectQty, 'defect_rate' => round($defectRate, 2), 'planned_hours' => 0, // 계획 공수 필드 없음 - 추후 확장 'actual_hours' => 0, 'efficiency_rate' => 0, 'active_worker_count' => $productionStats->active_worker_count ?? 0, 'issue_count' => 0, // work_order_issues 테이블 확인 필요 'on_time_delivery_count' => $onTimeCount, 'late_delivery_count' => max(0, $lateCount), 'delivery_rate' => round($deliveryRate, 2), ] ); return 1; } public function aggregateMonthly(int $tenantId, int $year, int $month): int { $dailyData = StatProductionDaily::where('tenant_id', $tenantId) ->whereYear('stat_date', $year) ->whereMonth('stat_date', $month) ->selectRaw(' SUM(wo_created_count) as wo_total_count, SUM(wo_completed_count) as wo_completed_count, SUM(production_qty) as production_qty, SUM(defect_qty) as defect_qty, SUM(planned_hours) as total_planned_hours, SUM(actual_hours) as total_actual_hours, SUM(issue_count) as issue_total_count ') ->first(); $productionQty = $dailyData->production_qty ?? 0; $defectQty = $dailyData->defect_qty ?? 0; $avgDefectRate = $productionQty > 0 ? ($defectQty / $productionQty) * 100 : 0; // 월평균 효율/납기 (일간 데이터의 평균) $avgRates = StatProductionDaily::where('tenant_id', $tenantId) ->whereYear('stat_date', $year) ->whereMonth('stat_date', $month) ->where('wo_completed_count', '>', 0) ->selectRaw(' AVG(efficiency_rate) as avg_efficiency_rate, AVG(delivery_rate) as avg_delivery_rate ') ->first(); StatProductionMonthly::updateOrCreate( ['tenant_id' => $tenantId, 'stat_year' => $year, 'stat_month' => $month], [ 'wo_total_count' => $dailyData->wo_total_count ?? 0, 'wo_completed_count' => $dailyData->wo_completed_count ?? 0, 'production_qty' => $productionQty, 'defect_qty' => $defectQty, 'avg_defect_rate' => round($avgDefectRate, 2), 'avg_efficiency_rate' => round($avgRates->avg_efficiency_rate ?? 0, 2), 'avg_delivery_rate' => round($avgRates->avg_delivery_rate ?? 0, 2), 'total_planned_hours' => $dailyData->total_planned_hours ?? 0, 'total_actual_hours' => $dailyData->total_actual_hours ?? 0, 'issue_total_count' => $dailyData->issue_total_count ?? 0, ] ); return 1; } }