format('Y-m-d'); // 재고 현황 (stocks 테이블 - 현재 스냅샷) $stockSummary = DB::connection('mysql') ->table('stocks') ->where('tenant_id', $tenantId) ->whereNull('deleted_at') ->selectRaw(' COUNT(*) as sku_count, COALESCE(SUM(stock_qty), 0) as total_qty, SUM(CASE WHEN stock_qty < safety_stock AND safety_stock > 0 THEN 1 ELSE 0 END) as below_safety, SUM(CASE WHEN stock_qty = 0 THEN 1 ELSE 0 END) as zero_stock, SUM(CASE WHEN stock_qty > safety_stock * 3 AND safety_stock > 0 THEN 1 ELSE 0 END) as excess_stock ') ->first(); // 입고 (stock_transactions type = 'receipt') $receiptStats = DB::connection('mysql') ->table('stock_transactions') ->where('tenant_id', $tenantId) ->whereDate('created_at', $dateStr) ->where('type', 'receipt') ->selectRaw('COUNT(*) as cnt, COALESCE(SUM(qty), 0) as total_qty') ->first(); // 출고 (stock_transactions type = 'issue') $issueStats = DB::connection('mysql') ->table('stock_transactions') ->where('tenant_id', $tenantId) ->whereDate('created_at', $dateStr) ->where('type', 'issue') ->selectRaw('COUNT(*) as cnt, COALESCE(SUM(ABS(qty)), 0) as total_qty') ->first(); // 품질검사 (inspections) $inspectionStats = DB::connection('mysql') ->table('inspections') ->where('tenant_id', $tenantId) ->where('inspection_date', $dateStr) ->whereNull('deleted_at') ->selectRaw(" COUNT(*) as cnt, SUM(CASE WHEN result = 'pass' THEN 1 ELSE 0 END) as pass_count, SUM(CASE WHEN result = 'fail' THEN 1 ELSE 0 END) as fail_count ") ->first(); $inspectionCount = $inspectionStats->cnt ?? 0; $passCount = $inspectionStats->pass_count ?? 0; $failCount = $inspectionStats->fail_count ?? 0; $passRate = $inspectionCount > 0 ? ($passCount / $inspectionCount) * 100 : 0; StatInventoryDaily::updateOrCreate( ['tenant_id' => $tenantId, 'stat_date' => $dateStr], [ 'total_sku_count' => $stockSummary->sku_count ?? 0, 'total_stock_qty' => $stockSummary->total_qty ?? 0, 'total_stock_value' => 0, // 단가 정보 없어 Phase 4에서 보완 'receipt_count' => $receiptStats->cnt ?? 0, 'receipt_qty' => $receiptStats->total_qty ?? 0, 'receipt_amount' => 0, 'issue_count' => $issueStats->cnt ?? 0, 'issue_qty' => $issueStats->total_qty ?? 0, 'issue_amount' => 0, 'below_safety_count' => $stockSummary->below_safety ?? 0, 'zero_stock_count' => $stockSummary->zero_stock ?? 0, 'excess_stock_count' => $stockSummary->excess_stock ?? 0, 'inspection_count' => $inspectionCount, 'inspection_pass_count' => $passCount, 'inspection_fail_count' => $failCount, 'inspection_pass_rate' => $passRate, 'turnover_rate' => 0, // 월간 집계에서 계산 ] ); return 1; } public function aggregateMonthly(int $tenantId, int $year, int $month): int { // 재고 도메인은 일간 스냅샷 기반이므로 별도 월간 테이블 없음 // 필요시 Phase 4에서 stat_inventory_monthly 추가 return 0; } }