json([ 'status' => 'success', 'type' => 'general', 'content' => $content ]); } /** * NDT Calculation (Test Packs) * * Test Paketleri için NDT hesaplama istatistiklerini getirir. * 'ndtCalculationTestPack' ayarını döndürür. * * @response 200 { * "status": "success", * "type": "test_packs", * "content": "..." * } */ public function ndtCalculationTestPacks() { $content = setting('ndtCalculationTestPack'); // Eğer content bir JSON string ise decode et $decoded = json_decode($content); if (json_last_error() === JSON_ERROR_NONE) { $content = $decoded; } return response()->json([ 'status' => 'success', 'type' => 'test_packs', 'content' => $content ]); } /** * NDT Calculation (Backlogs) * * Backloglar için NDT hesaplama istatistiklerini getirir. * 'ndtCalculationBacklogs' ayarını döndürür. * * @queryParam type string Filtreleme türü (opsiyonel). Example: backlogs * * @response 200 { * "status": "success", * "type": "backlogs", * "content": "..." * } */ public function ndtCalculationBacklogs() { request()->merge(['type' => 'backlogs']); $_GET['type'] = 'backlogs'; ob_start(); cacheBladeLoad('ndt-calculation'); $content = ob_get_clean(); // Eğer content bir JSON string ise decode et $decoded = json_decode($content); if (json_last_error() === JSON_ERROR_NONE) { $content = $decoded; } return response()->json([ 'status' => 'success', 'type' => 'backlogs', 'content' => $content ]); } /** * Dashboard Overview Stats * * Get aggregated statistics for project overview dashboard. * This endpoint is protected by a static hash. * Includes: * - WDI Monthly (Shop/Field/Total) * - Spool Progress * - NDT/Repair Backlog * - Hand-over Status * - Welding Equipment Status * * @header X-Api-Hash string required The static API hash for authentication. Example: your-secret-hash-key * * @response 200 { * "status": "success", * "data": { * "wdi_monthly": [...], * "spool_progress": [...], * "ndt_backlog": [...], * "handover_status": [...], * "welding_equipment": [...] * } * } */ public function dashboardStats(\Illuminate\Http\Request $request) { $year = $request->input('year', date('Y')); // 1. WDI Monthly Data (Global Aggregate) - Calendar Year $wdiMonthly = WeldLog::selectRaw(" DATE_FORMAT(welding_date, '%Y-%m') as month, MONTH(welding_date) as month_num, SUM(CASE WHEN type_of_joint = 'S' THEN nps_1 ELSE 0 END) as shop, SUM(CASE WHEN type_of_joint != 'S' OR type_of_joint IS NULL THEN nps_1 ELSE 0 END) as field, SUM(nps_1) as total ") ->whereNotNull('welding_date') // ->whereYear('welding_date', $year) // Removed to allow client-side filtering ->groupBy('month', 'month_num') ->orderBy('month', 'asc') ->get(); // 1b. WDI Monthly Data (By Project) $wdiByProjectRaw = WeldLog::selectRaw(" project, DATE_FORMAT(welding_date, '%Y-%m') as month, MONTH(welding_date) as month_num, SUM(CASE WHEN type_of_joint = 'S' THEN nps_1 ELSE 0 END) as shop, SUM(CASE WHEN type_of_joint != 'S' OR type_of_joint IS NULL THEN nps_1 ELSE 0 END) as field, SUM(nps_1) as total ") ->whereNotNull('welding_date') ->whereNotNull('project') ->where('project', '!=', '') ->whereYear('welding_date', $year) // Keep this for now or remove if needed, but it's unused by dashboard ->groupBy('project', 'month', 'month_num') ->get(); $wdiByProject = []; foreach($wdiByProjectRaw as $row) { // Use month_num (1-12) as key for the frontend table columns which expect 1..12 // For the new 'wdiByProject' which is custom for the table, we simply need the integer month key // because the table logic iterates 1 through 12. $wdiByProject[$row->project][$row->month_num] = [ 'shop' => (float)$row->shop, 'field' => (float)$row->field, 'total' => (float)$row->total, 'month_str' => $row->month // Useful for debugging or generic logic ]; } // 2. Spool Progress (Time-independent) // Logic: Group by iso_number + spool_number (Shop only), find effective status based on priority logic. // Priority: Waiting < Manufacturing < QC < Paint < Completed $statusCase = "CASE spool_status WHEN 'Waiting' THEN 1 WHEN 'Manufacturing' THEN 2 WHEN 'QC' THEN 3 WHEN 'Paint' THEN 4 WHEN 'Completed' THEN 5 ELSE 99 END"; $spoolStats = DB::table('weld_logs') ->selectRaw("status_val, COUNT(*) as count") ->fromSub(function($query) use ($statusCase) { $query->select('iso_number', 'spool_number') ->selectRaw("MIN($statusCase) as status_order") ->selectRaw(" CASE MIN($statusCase) WHEN 1 THEN 'Waiting' WHEN 2 THEN 'Manufacturing' WHEN 3 THEN 'QC' WHEN 4 THEN 'Paint' WHEN 5 THEN 'Completed' ELSE 'Other' END as status_val ") ->from('weld_logs') ->where('type_of_joint', 'S') // Shop joints drive spool status ->whereNotNull('spool_number') ->where('spool_number', '!=', '') ->whereNotNull('welding_date') ->groupBy('iso_number', 'spool_number'); }, 'sub') ->groupBy('status_val') ->get(); // Transform to array format for frontend $spoolProgress = []; foreach ($spoolStats as $row) { $spoolProgress[] = [ 'status' => $row->status_val, 'count' => (int)$row->count ]; } // 3. NDT/Repair Backlog // Using existing setting logic from ndt-backlog.blade.php $ndtData = json_decode(setting("ndtCalculation"), true); $ndtBacklogStats = []; if (is_array($ndtData)) { foreach($ndtData as $d) { foreach($d as $field => $value) { if(strpos($field, "backlog") !== false) { $key = strtoupper(str_replace("_backlog", "", $field)); if(!isset($ndtBacklogStats[$key])) { $ndtBacklogStats[$key] = 0; } if($value > 0) { $ndtBacklogStats[$key] += $value; } } } } } // Add dynamic limits from Settings $ndtBacklogStats['ndt_limit'] = setting('ndt_limit'); $ndtBacklogStats['ndt_wdi'] = setting('ndt_wdi'); $ndtBacklogStats['repair_limit'] = setting('repair_limit'); $ndtBacklogStats['repair_wdi'] = setting('repair_wdi'); // 4. Hand-over Status // Statuses: Dynamically fetched from id_status $handoverProgress = Handover::selectRaw('id_status, COUNT(*) as count') ->whereNotNull('id_status') ->where('id_status', '!=', '') ->groupBy('id_status') ->pluck('count', 'id_status') ->toArray(); // 5. Welding Equipment Status // Statuses: Working, Repair, Out of use $equipmentStats = WeldingEquipment::selectRaw('working_status, COUNT(*) as count') ->whereNotNull('working_status') ->groupBy('working_status') ->get() ->pluck('count', 'working_status'); $targetEquipmentStatuses = ['Working', 'Repair', 'Out of use']; $equipmentProgress = []; foreach ($targetEquipmentStatuses as $status) { $equipmentProgress[$status] = $equipmentStats[$status] ?? 0; } // Add others $otherEquipmentCount = 0; foreach ($equipmentStats as $status => $count) { if (!in_array($status, $targetEquipmentStatuses)) { $otherEquipmentCount += $count; } } if ($otherEquipmentCount > 0) { $equipmentProgress['Other'] = $otherEquipmentCount; } // 6. Welding Status (Welder Distribution by Site) // Logic: Group by project (Site). // CS = distinct real_welder_1 & real_welder_2 where ru_material_group_1 matches 'M01' // SS/AS = distinct real_welder_1 & real_welder_2 where ru_material_group_1 does NOT match 'M01' $weldingStatusData = DB::table(function ($query) { $query->select('project', 'type_of_joint', 'ru_material_group_1', 'real_welder_1 as welder', 'welding_date') ->from('weld_logs') ->whereNotNull('real_welder_1') ->where('real_welder_1', '!=', '') ->whereNotNull('welding_date') ->unionAll( DB::table('weld_logs') ->select('project', 'type_of_joint', 'ru_material_group_2', 'real_welder_1 as welder', 'welding_date') ->from('weld_logs') ->whereNotNull('real_welder_2') ->where('real_welder_2', '!=', '') ->whereNotNull('welding_date') ); }, 'u') ->select('project') ->selectRaw("YEAR(welding_date) as year") ->selectRaw("MONTH(welding_date) as month") ->selectRaw("COUNT(DISTINCT CASE WHEN type_of_joint = 'S' THEN welder END) as shop") ->selectRaw("COUNT(DISTINCT CASE WHEN type_of_joint != 'S' OR type_of_joint IS NULL THEN welder END) as field") ->selectRaw("COUNT(DISTINCT CASE WHEN (ru_material_group_1 LIKE '%M01%' OR ru_material_group_1 LIKE '%М01%') THEN welder END) as cs") ->selectRaw("COUNT(DISTINCT CASE WHEN (ru_material_group_1 NOT LIKE '%M01%' AND ru_material_group_1 NOT LIKE '%М01%') OR ru_material_group_1 IS NULL THEN welder END) as ss") ->selectRaw("COUNT(DISTINCT welder) as actual_total") ->whereNotNull('project') ->where('project', '!=', '') ->groupBy('project', 'year', 'month') ->get(); $weldingStatus = []; foreach($weldingStatusData as $row) { $weldingStatus[] = [ 'site' => $row->project, 'year' => (int)$row->year, 'month' => (int)$row->month, 'shop' => (int)$row->shop, 'field' => (int)$row->field, 'cs' => (int)$row->cs, 'ss' => (int)$row->ss, 'total' => (int)$row->actual_total ]; } // 7. Welding Status Overall Summary (No Site Breakdown) // Count unique welders across ALL projects for Shop and Field // 7. Welding Status Overall Summary (No Site Breakdown) // Count unique welders across ALL projects for Shop and Field $weldingSummaryRaw = DB::table(function ($query) { $query->select('type_of_joint', 'real_welder_1 as welder') ->from('weld_logs') ->whereNotNull('real_welder_1') ->where('real_welder_1', '!=', '') ->whereNotNull('welding_date') ->unionAll( DB::table('weld_logs') ->select('type_of_joint', 'real_welder_2 as welder') ->from('weld_logs') ->whereNotNull('real_welder_2') ->where('real_welder_2', '!=', '') ->whereNotNull('welding_date') ); }, 'u') ->selectRaw("COUNT(DISTINCT CASE WHEN type_of_joint = 'S' THEN welder END) as shop") ->selectRaw("COUNT(DISTINCT CASE WHEN type_of_joint != 'S' OR type_of_joint IS NULL THEN welder END) as field") ->selectRaw("COUNT(DISTINCT welder) as actual_total") ->first(); $weldingStatusSummary = [ 'shop' => (int)$weldingSummaryRaw->shop, 'field' => (int)$weldingSummaryRaw->field, 'total' => (int)$weldingSummaryRaw->actual_total ]; // 8. Welding Status Monthly (Shop/Field/Total per Month) // Groups distinct welders by month and project // 8. Welding Status Monthly (Shop/Field/Total per Month) // Groups distinct welders by month (Global Site Scope) $weldingStatusMonthly = DB::table(function ($query) use ($year) { $query->select('project', 'welding_date', 'type_of_joint', 'real_welder_1 as welder') ->from('weld_logs') ->whereNotNull('real_welder_1') ->where('real_welder_1', '!=', '') ->whereNotNull('welding_date') ->unionAll( DB::table('weld_logs') ->select('project', 'welding_date', 'type_of_joint', 'real_welder_2 as welder') ->from('weld_logs') ->whereNotNull('real_welder_2') ->where('real_welder_2', '!=', '') ->whereNotNull('welding_date') ); }, 'u') ->selectRaw(" DATE_FORMAT(welding_date, '%Y-%m') as month_key, MONTH(welding_date) as month, YEAR(welding_date) as year, COUNT(DISTINCT CASE WHEN type_of_joint = 'S' THEN welder END) as shop, COUNT(DISTINCT CASE WHEN type_of_joint != 'S' OR type_of_joint IS NULL THEN welder END) as field, COUNT(DISTINCT welder) as actual_total ") ->groupBy('month_key', 'month', 'year') ->orderBy('month_key', 'asc') ->get() ->map(function($item) { return [ 'project' => 'Total', // Value is now aggregated for the whole site 'month_key' => $item->month_key, 'month' => (int)$item->month, 'year' => (int)$item->year, 'shop' => (int)$item->shop, 'field' => (int)$item->field, 'total' => (int)$item->actual_total ]; }); // 9. NDT Summary (RT & UT) // New calculation logic mirrored from summary-nocache.blade.php $ndtSummary = []; // RT Logic $rtTotal = DB::table('radiographic_tests')->whereNotNull('rt_result')->count(); $rtWDI = DB::table('radiographic_tests')->whereNotNull('rt_result')->sum('nps_1'); $rtAccepted = DB::table('radiographic_tests')->where('rt_result', 'Accept / Годен')->count(); $rtRejected = DB::table('radiographic_tests')->whereNotNull('rt_result')->whereNotIn('rt_result', ['', 'Accept / Годен'])->count(); // RT Repair Rate logic: (ShopRepair + FieldRepair) / (ShopTotal + FieldTotal) $rtShopTotal = DB::table('radiographic_tests')->where('type_of_joint', 'S')->whereNotIn('rt_result', [''])->count(); $rtShopRepair = DB::table('radiographic_tests')->where('type_of_joint', 'S')->whereNotIn('rt_result', ['', 'Accept / Годен'])->count(); $rtFieldTotal = DB::table('radiographic_tests')->where('type_of_joint', 'F')->whereNotIn('rt_result', [''])->count(); $rtFieldRepair = DB::table('radiographic_tests')->where('type_of_joint', 'F')->whereNotIn('rt_result', ['', 'Accept / Годен'])->count(); $rtRepairRate = ($rtShopTotal + $rtFieldTotal) > 0 ? round(($rtShopRepair + $rtFieldRepair) * 100 / ($rtShopTotal + $rtFieldTotal), 2) : 0; $ndtSummary['rt'] = [ 'total' => $rtTotal, 'wdi' => (float)$rtWDI, 'accepted' => $rtAccepted, 'rejected' => $rtRejected, 'repair_rate' => $rtRepairRate ]; // UT Logic $utTotal = DB::table('ultrasonic_tests')->whereNotNull('ut_result')->count(); $utWDI = DB::table('ultrasonic_tests')->whereNotNull('ut_result')->sum('nps_1'); $utAccepted = DB::table('ultrasonic_tests')->where('ut_result', 'Accept / Годен')->count(); $utRejected = DB::table('ultrasonic_tests')->whereNotNull('ut_result')->where('ut_result', '!=', 'Accept / Годен')->count(); // UT Repair Rate $utShopTotal = DB::table('ultrasonic_tests')->where('type_of_joint', 'S')->whereNotIn('ut_result', [''])->count(); $utShopRepair = DB::table('ultrasonic_tests')->where('type_of_joint', 'S')->whereNotIn('ut_result', ['', 'Accept / Годен'])->count(); $utFieldTotal = DB::table('ultrasonic_tests')->where('type_of_joint', 'F')->whereNotIn('ut_result', [''])->count(); $utFieldRepair = DB::table('ultrasonic_tests')->where('type_of_joint', 'F')->whereNotIn('ut_result', ['', 'Accept / Годен'])->count(); $utRepairRate = ($utShopTotal + $utFieldTotal) > 0 ? round(($utShopRepair + $utFieldRepair) * 100 / ($utShopTotal + $utFieldTotal), 2) : 0; $ndtSummary['ut'] = [ 'total' => $utTotal, 'wdi' => (float)$utWDI, 'accepted' => $utAccepted, 'rejected' => $utRejected, 'repair_rate' => $utRepairRate ]; // 10. WDI Statistics (CS vs SS/AS) by Project // CS: Material Group contains M01 // SS: Material Group does not contain M01 $wdiStatsData = DB::table('weld_logs') ->select('project') ->selectRaw("YEAR(welding_date) as year") ->selectRaw("MONTH(welding_date) as month") ->selectRaw("SUM(CASE WHEN (ru_material_group_1 LIKE '%M01%' OR ru_material_group_1 LIKE '%М01%') THEN nps_1 ELSE 0 END) as cs_wdi") ->selectRaw("SUM(CASE WHEN (ru_material_group_1 NOT LIKE '%M01%' AND ru_material_group_1 NOT LIKE '%М01%') OR ru_material_group_1 IS NULL THEN nps_1 ELSE 0 END) as ss_wdi") ->whereNotNull('welding_date') ->where('project', '!=', '') ->whereNotNull('project') ->groupBy('project', 'year', 'month') ->get(); $wdiStats = []; foreach($wdiStatsData as $row) { $wdiStats[] = [ 'project' => $row->project, 'year' => (int)$row->year, 'month' => (int)$row->month, 'cs' => (float)$row->cs_wdi, 'ss' => (float)$row->ss_wdi, 'total' => (float)$row->cs_wdi + (float)$row->ss_wdi ]; } // 11. Weld Backlog (Prefab vs Field) by Project // Backlog: Items with no welding date, grouped by year/month from fit_up_date for frontend filtering $weldBacklogData = DB::table('weld_logs') ->select('project') ->selectRaw("SUM(CASE WHEN type_of_joint = 'S' THEN nps_1 ELSE 0 END) as prefab_backlog") ->selectRaw("SUM(CASE WHEN type_of_joint != 'S' OR type_of_joint IS NULL THEN nps_1 ELSE 0 END) as field_backlog") ->selectRaw("SUM(nps_1) as total_backlog") ->whereNull('welding_date') ->where('project', '!=', '') ->whereNotNull('project') ->groupBy('project') ->get(); $weldBacklog = []; foreach($weldBacklogData as $row) { $weldBacklog[] = [ 'project' => $row->project, 'prefab' => (float)$row->prefab_backlog, 'field' => (float)$row->field_backlog, 'total' => (float)$row->total_backlog ]; } // 12. Welder Current (from naks_welders table) // Total unique welders at each site, categorized by CS/SS // CS: material_1 contains М01 or M01 // SS: material_1 does NOT contain М01/M01 // No date filter - shows total registered welders per site $welderCurrentData = DB::table('welder_tests') ->selectRaw("COUNT(DISTINCT CASE WHEN (material_group_1 LIKE '%M01%' OR material_group_1 LIKE '%М01%') THEN naks_id END) as cs") ->selectRaw("COUNT(DISTINCT CASE WHEN (material_group_1 NOT LIKE '%M01%' AND material_group_1 NOT LIKE '%М01%') OR material_group_1 IS NULL THEN naks_id END) as ss") ->selectRaw("COUNT(DISTINCT naks_id) as total") ->where('status', 'Active') ->get(); $welderCurrent = []; foreach($welderCurrentData as $row) { $welderCurrent[] = [ 'project' => '', 'cs' => (int)$row->cs, 'ss' => (int)$row->ss, 'total' => (int)$row->total ]; } return response()->json([ 'status' => 'success', 'data' => [ 'wdi_monthly' => $wdiMonthly, 'spool_progress' => $spoolProgress, 'ndt_backlog' => $ndtBacklogStats, 'handover_status' => $handoverProgress, 'welding_equipment' => $equipmentProgress, 'welding_status' => $weldingStatus, 'welding_status_monthly' => $weldingStatusMonthly, 'welding_status_summary' => $weldingStatusSummary, 'ndt_summary' => $ndtSummary, 'wdi_by_project' => $wdiByProject, 'wdi_stats' => $wdiStats, 'weld_backlog' => $weldBacklog, 'repair_stats_monthly' => DB::table('weld_logs') ->selectRaw(" DATE_FORMAT(welding_date, '%Y-%m') as month_key, YEAR(welding_date) as year, MONTH(welding_date) as month, SUM(CASE WHEN type_of_joint = 'S' AND rt_result IS NOT NULL AND rt_result != '' THEN 1 ELSE 0 END) as rt_shop_total, SUM(CASE WHEN type_of_joint = 'S' AND rt_result NOT IN ('', 'Accept / Годен') THEN 1 ELSE 0 END) as rt_shop_repair, SUM(CASE WHEN type_of_joint = 'F' AND rt_result IS NOT NULL AND rt_result != '' THEN 1 ELSE 0 END) as rt_field_total, SUM(CASE WHEN type_of_joint = 'F' AND rt_result NOT IN ('', 'Accept / Годен') THEN 1 ELSE 0 END) as rt_field_repair, SUM(CASE WHEN type_of_joint = 'S' AND ut_result IS NOT NULL AND ut_result != '' THEN 1 ELSE 0 END) as ut_shop_total, SUM(CASE WHEN type_of_joint = 'S' AND ut_result NOT IN ('', 'Accept / Годен') THEN 1 ELSE 0 END) as ut_shop_repair, SUM(CASE WHEN type_of_joint = 'F' AND ut_result IS NOT NULL AND ut_result != '' THEN 1 ELSE 0 END) as ut_field_total, SUM(CASE WHEN type_of_joint = 'F' AND ut_result NOT IN ('', 'Accept / Годен') THEN 1 ELSE 0 END) as ut_field_repair ") ->whereNotNull('welding_date') ->groupBy('month_key', 'year', 'month') ->get(), 'test_packages' => DB::table('test_packages') ->leftJoinSub(function ($query) { $query->from('weld_logs') ->select( 'test_package_no', DB::raw('MAX(project) as project'), DB::raw('MAX(welding_date) as last_weld_date') ) ->whereNotNull('test_package_no') ->where('test_package_no', '!=', '') ->whereNotNull('welding_date') ->groupBy('test_package_no'); }, 'projects', 'projects.test_package_no', '=', 'test_packages.test_package_number') ->selectRaw(" COALESCE(projects.project, 'Unknown') as project, count(test_packages.id) as total_tp, SUM(CASE WHEN walkdown_date IS NOT NULL THEN 1 ELSE 0 END) as line_check, SUM(COALESCE(a_punch_point_open, 0)) as punch_a, SUM(CASE WHEN test_date IS NOT NULL THEN 1 ELSE 0 END) as test, SUM(CASE WHEN reinstatement_date IS NOT NULL THEN 1 ELSE 0 END) as reinstatement ") ->groupBy( DB::raw("COALESCE(projects.project, 'Unknown')") ) ->get(), 'welder_current' => $welderCurrent, ] ]); } /** * Welding Status By Project * * Get summary data for "Sahaya Göre Kaynak Durumu" (Welding Status by Site). * Breakdown of WDI (NPS-1) by project, grouped into SHOP and FIELD. * * @response 200 { * "status": "success", * "data": [ * { * "project": "COOLER TOWER", * "shop": 82, * "field": 122, * "total": 204, * "percentage": 93 * }, * ... * ] * } */ public function weldingByProject() { $weldingData = DB::table('weld_logs') ->select('project') ->selectRaw("SUM(CASE WHEN type_of_joint = 'S' AND welding_date IS NOT NULL THEN nps_1 ELSE 0 END) as shop") ->selectRaw("SUM(CASE WHEN (type_of_joint != 'S' OR type_of_joint IS NULL) AND welding_date IS NOT NULL THEN nps_1 ELSE 0 END) as field") ->selectRaw("SUM(CASE WHEN welding_date IS NOT NULL THEN nps_1 ELSE 0 END) as total_welded") ->selectRaw("SUM(nps_1) as total_scope") ->whereNotNull('project') ->where('project', '!=', '') ->groupBy('project') ->get(); $result = $weldingData->map(function ($row) { $totalWelded = (float)$row->total_welded; $totalScope = (float)$row->total_scope; $percentage = $totalScope > 0 ? round(($totalWelded / $totalScope) * 100) : 0; return [ 'project' => $row->project, 'shop' => (float)$row->shop, 'field' => (float)$row->field, 'total' => $totalWelded, 'percentage' => (int)$percentage ]; }); return response()->json([ 'status' => 'success', 'data' => $result ]); } }