query("SELECT tanggal_lahir, status_bekerja, dapat_bekerja, kondisi_kesehatan FROM anggota_keluarga WHERE rumah_warga_id = $wargaId"); $anggota = []; while ($r = $res->fetch_assoc()) $anggota[] = $r; $adaProduktif = false; // usia 18-55 th $adaProduktifBisaKerja = false; // produktif & bisa kerja & belum bekerja tetap $semuaProduktifTakBisa = true; // semua yg produktif tidak bisa kerja foreach ($anggota as $a) { $usia = null; if ($a['tanggal_lahir']) { $lahir = new DateTime($a['tanggal_lahir']); $usia = (int)$today->diff($lahir)->y; } if ($usia !== null && $usia >= 18 && $usia <= 55) { $adaProduktif = true; $bisaKerja = (int)$a['dapat_bekerja'] === 1 && in_array($a['kondisi_kesehatan'], ['sehat', 'sakit_ringan']); if ($bisaKerja) $semuaProduktifTakBisa = false; // Produktif, bisa kerja, belum bekerja tetap → kandidat pelatihan if ($bisaKerja && $a['status_bekerja'] !== 'bekerja') { $adaProduktifBisaKerja = true; } } } // Kriteria bantuan rutin $perluRutin = !$adaProduktif || ($adaProduktif && $semuaProduktifTakBisa) || $penghasilan < 500000 || $statusPekerjaan === 'tidak_bekerja'; // Kriteria bantuan pelatihan $perluPelatihan = $adaProduktifBisaKerja; if ($perluRutin && $perluPelatihan) return 'keduanya'; if ($perluRutin) return 'bantuan_rutin'; if ($perluPelatihan) return 'bantuan_pelatihan'; return 'tidak_ada'; } // ════════════════════════════════════════════════════════════════════════════ // FUNGSI: Hitung kategori kesejahteraan otomatis berdasarkan data warga // ════════════════════════════════════════════════════════════════════════════ function hitungKategoriKesejahteraan(float $penghasilan, int $tanggungan, string $statusTempatTinggal, string $sumberAir): string { // Logika sederhana: // Jika penghasilan sangat rendah dan tanggungan banyak $pendapatanPerKapita = $tanggungan > 0 ? $penghasilan / $tanggungan : $penghasilan; $skor = 0; if ($pendapatanPerKapita < 300000) { $skor += 40; } elseif ($pendapatanPerKapita < 600000) { $skor += 20; } if ($statusTempatTinggal === 'numpang' || $statusTempatTinggal === 'sewa') { $skor += 20; } if ($sumberAir === 'sungai' || $sumberAir === 'hujan') { $skor += 10; } if ($skor >= 50) return 'sangat_miskin'; if ($skor >= 20) return 'miskin'; return 'rentan_miskin'; } // ════════════════════════════════════════════════════════════════════════════ // FUNGSI: Memeriksa akses koordinator berdasarkan kelurahan/kecamatan ATAU radius geografis <= 5km // ════════════════════════════════════════════════════════════════════════════ function checkCoordinatorAccess(mysqli $conn, int $koorIbadahId, float $lat, float $lng, string $kelurahan, string $kecamatan): bool { $wilRes = $conn->query("SELECT latitude, longitude, kelurahan, kecamatan FROM rumah_ibadah WHERE id = $koorIbadahId"); if ($wil = $wilRes->fetch_assoc()) { // Cek 1: Pencocokan nama kelurahan/kecamatan (sebagai fallback tangguh) $kel = strtolower($wil['kelurahan']); $kec = strtolower($wil['kecamatan']); $wKel = strtolower(trim($kelurahan)); $wKec = strtolower(trim($kecamatan)); if (($kel !== '' && strpos($wKel, $kel) !== false) || ($kec !== '' && strpos($wKec, $kec) !== false)) { return true; } // Cek 2: Jarak radius geografis <= 5000m (karena batas slider maksimal 5km) $lat1 = (float)$wil['latitude']; $lng1 = (float)$wil['longitude']; if ($lat1 !== 0.0 && $lng1 !== 0.0 && $lat !== 0.0 && $lng !== 0.0) { $earthRadius = 6371000; $dLat = deg2rad($lat - $lat1); $dLng = deg2rad($lng - $lng1); $a = sin($dLat/2) * sin($dLat/2) + cos(deg2rad($lat1)) * cos(deg2rad($lat)) * sin($dLng/2) * sin($dLng/2); $c = 2 * atan2(sqrt($a), sqrt(1-$a)); $dist = $earthRadius * $c; if ($dist <= 5000.0) { return true; } } } return false; } // ════════════════════════════════════════════════════════════════════════════ switch ($method) { // ── GET ────────────────────────────────────────────────────────────────── case 'GET': // Izinkan guest mode (opsional) untuk melihat data rumah warga $currentUser = optionalAuth(); $isGuest = $currentUser === null; // Jika tidak login (guest), validasi akses if ($isGuest) { $currentUser = ['role' => 'guest']; } else { // Validasi role jika sudah login if (!in_array($currentUser['role'], ['admin', 'petugas', 'koordinator', 'kontributor'])) { http_response_code(403); echo json_encode(['success' => false, 'message' => 'Forbidden: Role Anda tidak memiliki akses.']); exit; } } if (isset($_GET['id'])) { $id = (int)$_GET['id']; $res = $conn->query("SELECT * FROM rumah_warga WHERE id=$id"); if ($row = $res->fetch_assoc()) { // Batasi Koordinator agar hanya bisa melihat detail warga di wilayah tugasnya sendiri (radius / kelurahan / kecamatan) if ($currentUser['role'] === 'koordinator') { $koorIbadahId = (int)$currentUser['rumah_ibadah_id']; $lat = (float)($row['latitude'] ?? 0); $lng = (float)($row['longitude'] ?? 0); $kelurahan = $row['kelurahan'] ?? ''; $kecamatan = $row['kecamatan'] ?? ''; if (!checkCoordinatorAccess($conn, $koorIbadahId, $lat, $lng, $kelurahan, $kecamatan)) { http_response_code(403); echo json_encode(['success' => false, 'message' => 'Forbidden: Detail warga di luar wilayah tugas Anda.']); exit; } } // Sertakan anggota keluarga jika bukan koordinator / kontributor / guest if ($currentUser['role'] === 'koordinator') { $row['anggota'] = []; unset($row['no_kk']); unset($row['nik']); unset($row['penghasilan_bulanan']); unset($row['pekerjaan']); unset($row['status_pekerjaan']); unset($row['luas_rumah']); unset($row['jenis_lantai']); unset($row['jenis_dinding']); unset($row['sumber_air']); unset($row['status_tempat_tinggal']); unset($row['keterangan']); unset($row['skor_kepercayaan']); unset($row['is_rekomendasi_manual']); unset($row['is_kategori_manual']); // KEEP: kategori_kemiskinan, jumlah_tanggungan (needed for display) } elseif ($currentUser['role'] === 'kontributor' || $currentUser['role'] === 'guest') { $row['anggota'] = []; unset($row['no_kk']); unset($row['nik']); unset($row['penghasilan_bulanan']); unset($row['pekerjaan']); unset($row['status_pekerjaan']); unset($row['luas_rumah']); unset($row['jenis_lantai']); unset($row['jenis_dinding']); unset($row['sumber_air']); unset($row['status_tempat_tinggal']); unset($row['keterangan']); unset($row['skor_kepercayaan']); unset($row['is_rekomendasi_manual']); unset($row['is_kategori_manual']); unset($row['created_at']); unset($row['updated_at']); // KEEP: kategori_kemiskinan, jumlah_tanggungan, nama_kepala_keluarga (needed for display) } else { $resA = $conn->query("SELECT * FROM anggota_keluarga WHERE rumah_warga_id=$id ORDER BY tanggal_lahir ASC"); $anggota = []; while ($a = $resA->fetch_assoc()) $anggota[] = $a; $row['anggota'] = $anggota; } echo json_encode(['success' => true, 'data' => $row]); } else { echo json_encode(['success' => false, 'message' => 'Data tidak ditemukan']); } break; } // Filter pencarian $where = []; if (!empty($_GET['kategori'])) $where[] = "kategori_kemiskinan = '" . $conn->real_escape_string($_GET['kategori']) . "'"; if (!empty($_GET['status'])) $where[] = "status_bantuan = '" . $conn->real_escape_string($_GET['status']) . "'"; if (!empty($_GET['rekomendasi'])) $where[] = "rekomendasi_bantuan = '" . $conn->real_escape_string($_GET['rekomendasi']) . "'"; if (!empty($_GET['kecamatan'])) $where[] = "kecamatan LIKE '%" . $conn->real_escape_string($_GET['kecamatan']) . "%'"; if (!empty($_GET['kelurahan'])) $where[] = "kelurahan LIKE '%" . $conn->real_escape_string($_GET['kelurahan']) . "%'"; if (!empty($_GET['search'])) $where[] = "(nama_kepala_keluarga LIKE '%" . $conn->real_escape_string($_GET['search']) . "%' OR no_kk LIKE '%" . $conn->real_escape_string($_GET['search']) . "%' OR nik LIKE '%" . $conn->real_escape_string($_GET['search']) . "%')"; // Filter wilayah untuk koordinator tidak lagi dibatasi per kelurahan/kecamatan di SQL, // melainkan akan difilter secara dinamis berdasarkan radius geografis di frontend. $sql = "SELECT * FROM rumah_warga" . ($where ? " WHERE " . implode(' AND ', $where) : "") . " ORDER BY nama_kepala_keluarga ASC"; $result = $conn->query($sql); if ($result === false) { echo json_encode([ 'success' => false, 'message' => 'Gagal mengambil data dari tabel "rumah_warga": ' . $conn->error ]); break; } $data = []; while ($row = $result->fetch_assoc()) { if ($currentUser['role'] === 'koordinator') { unset($row['no_kk']); unset($row['nik']); unset($row['penghasilan_bulanan']); unset($row['pekerjaan']); unset($row['status_pekerjaan']); unset($row['luas_rumah']); unset($row['jenis_lantai']); unset($row['jenis_dinding']); unset($row['sumber_air']); unset($row['status_tempat_tinggal']); unset($row['keterangan']); unset($row['skor_kepercayaan']); unset($row['is_rekomendasi_manual']); // KEEP: kategori_kemiskinan, jumlah_tanggungan (needed for frontend display) } elseif ($currentUser['role'] === 'kontributor' || $currentUser['role'] === 'guest') { unset($row['no_kk']); unset($row['nik']); unset($row['penghasilan_bulanan']); unset($row['pekerjaan']); unset($row['status_pekerjaan']); unset($row['luas_rumah']); unset($row['jenis_lantai']); unset($row['jenis_dinding']); unset($row['sumber_air']); unset($row['status_tempat_tinggal']); unset($row['keterangan']); unset($row['skor_kepercayaan']); unset($row['is_rekomendasi_manual']); unset($row['created_at']); unset($row['updated_at']); // KEEP: kategori_kemiskinan, jumlah_tanggungan, nama_kepala_keluarga (needed for frontend display) } $data[] = $row; } echo json_encode(['success' => true, 'data' => $data, 'total' => count($data)]); break; // ── POST ───────────────────────────────────────────────────────────────── case 'POST': // Membatasi akses: Hanya Admin dan Petugas Lapangan yang bisa menginput requireAuth(['admin', 'petugas']); $input = json_decode(file_get_contents('php://input'), true) ?: $_POST; $no_kk = $conn->real_escape_string(trim($input['no_kk'] ?? '')); $nama = $conn->real_escape_string(trim($input['nama_kepala_keluarga'] ?? '')); $nik = $conn->real_escape_string($input['nik'] ?? ''); $alamat = $conn->real_escape_string($input['alamat'] ?? ''); $kelurahan = $conn->real_escape_string($input['kelurahan'] ?? ''); $kecamatan = $conn->real_escape_string($input['kecamatan'] ?? ''); $kota = $conn->real_escape_string($input['kota'] ?? ''); $lat = (float)($input['latitude'] ?? 0); $lng = (float)($input['longitude'] ?? 0); $tanggungan = (int)($input['jumlah_tanggungan'] ?? 1); $penghasilan= (float)($input['penghasilan_bulanan'] ?? 0); $pekerjaan = $conn->real_escape_string($input['pekerjaan'] ?? ''); $stsPekerjaan = $conn->real_escape_string($input['status_pekerjaan'] ?? 'tidak_bekerja'); $luas = !empty($input['luas_rumah']) ? (float)$input['luas_rumah'] : 'NULL'; $lantai = $conn->real_escape_string($input['jenis_lantai'] ?? 'semen'); $dinding = $conn->real_escape_string($input['jenis_dinding'] ?? 'tembok'); $air = $conn->real_escape_string($input['sumber_air'] ?? 'sumur'); $tinggal = $conn->real_escape_string($input['status_tempat_tinggal'] ?? 'milik_sendiri'); $kategoriInput = $conn->real_escape_string($input['kategori_kemiskinan'] ?? 'miskin'); $isKatManual = ($kategoriInput !== 'sistem') ? 1 : 0; $kategori = ($isKatManual === 1) ? $kategoriInput : hitungKategoriKesejahteraan($penghasilan, $tanggungan, $tinggal, $air); $stsbansos = $conn->real_escape_string($input['status_bantuan'] ?? 'belum_terima'); $ket = $conn->real_escape_string($input['keterangan'] ?? ''); $dikelola = !empty($input['dikelola_oleh_ibadah_id']) ? (int)$input['dikelola_oleh_ibadah_id'] : 'NULL'; // Validasi if (!$nama || !$lat || !$lng) { echo json_encode(['success' => false, 'message' => 'Nama KK, latitude, dan longitude wajib diisi']); break; } if (!$no_kk || strlen(preg_replace('/\D/', '', $no_kk)) !== 16) { echo json_encode(['success' => false, 'message' => 'Nomor KK harus 16 digit angka']); break; } // Cek duplikat KK $cek = $conn->query("SELECT id FROM rumah_warga WHERE no_kk='$no_kk'"); if ($cek->num_rows > 0) { echo json_encode(['success' => false, 'message' => 'Nomor KK sudah terdaftar']); break; } $luasVal = is_numeric($luas) ? $luas : 'NULL'; $rekInput = $conn->real_escape_string($input['rekomendasi_bantuan'] ?? 'sistem'); $isManual = ($rekInput !== 'sistem') ? 1 : 0; $sql = "INSERT INTO rumah_warga (no_kk,nama_kepala_keluarga,nik,alamat,kelurahan,kecamatan,kota,latitude,longitude, jumlah_tanggungan,penghasilan_bulanan,pekerjaan,status_pekerjaan, luas_rumah,jenis_lantai,jenis_dinding,sumber_air,status_tempat_tinggal, kategori_kemiskinan,is_kategori_manual,status_bantuan,rekomendasi_bantuan,is_rekomendasi_manual,keterangan, dikelola_oleh_ibadah_id, tanggal_verifikasi, skor_kepercayaan, status_verifikasi) VALUES ('$no_kk','$nama','$nik','$alamat','$kelurahan','$kecamatan','$kota',$lat,$lng, $tanggungan,$penghasilan,'$pekerjaan','$stsPekerjaan', $luasVal,'$lantai','$dinding','$air','$tinggal', '$kategori',$isKatManual,'$stsbansos','bantuan_rutin',$isManual,'$ket', $dikelola, CURRENT_DATE(), 100, 'terverifikasi')"; if ($conn->query($sql)) { $newId = $conn->insert_id; // Simpan anggota jika ada if (!empty($input['anggota']) && is_array($input['anggota'])) { simpanAnggota($conn, $newId, $input['anggota']); } // Hitung rekomendasi otomatis if ($isManual === 1) { $rek = $rekInput; } else { $rek = hitungRekomendasi($conn, $newId, $penghasilan, $stsPekerjaan); } $conn->query("UPDATE rumah_warga SET rekomendasi_bantuan='$rek' WHERE id=$newId"); echo json_encode(['success' => true, 'message' => 'Data berhasil disimpan', 'id' => $newId, 'rekomendasi' => $rek]); } else { echo json_encode(['success' => false, 'message' => 'Gagal menyimpan: ' . $conn->error]); } break; // ── PUT ────────────────────────────────────────────────────────────────── case 'PUT': // Membatasi akses: Admin, Petugas Lapangan, dan Koordinator Rumah Ibadah $currentUser = requireAuth(['admin', 'petugas', 'koordinator']); $input = json_decode(file_get_contents('php://input'), true); $id = (int)($input['id'] ?? 0); if (!$id) { echo json_encode(['success' => false, 'message' => 'ID tidak valid']); break; } if ($currentUser['role'] === 'koordinator') { $stsbansos = $conn->real_escape_string($input['status_bantuan'] ?? 'belum_terima'); // Cek apakah warga berada di wilayah tugas koordinator (radius / kelurahan / kecamatan) $koorIbadahId = (int)$currentUser['rumah_ibadah_id']; $wargaRes = $conn->query("SELECT latitude, longitude, kelurahan, kecamatan FROM rumah_warga WHERE id = $id"); if ($wargaRow = $wargaRes->fetch_assoc()) { $lat = (float)($wargaRow['latitude'] ?? 0); $lng = (float)($wargaRow['longitude'] ?? 0); $kelurahan = $wargaRow['kelurahan'] ?? ''; $kecamatan = $wargaRow['kecamatan'] ?? ''; if (!checkCoordinatorAccess($conn, $koorIbadahId, $lat, $lng, $kelurahan, $kecamatan)) { http_response_code(403); echo json_encode(['success' => false, 'message' => 'Forbidden: Warga di luar wilayah tugas Anda.']); exit; } } else { echo json_encode(['success' => false, 'message' => 'Warga tidak ditemukan.']); exit; } $sql = "UPDATE rumah_warga SET status_bantuan='$stsbansos' WHERE id=$id"; if ($conn->query($sql)) { echo json_encode(['success' => true, 'message' => 'Status bantuan warga berhasil diperbarui']); } else { echo json_encode(['success' => false, 'message' => 'Gagal memperbarui status bantuan: ' . $conn->error]); } break; } $no_kk = $conn->real_escape_string(trim($input['no_kk'] ?? '')); $nama = $conn->real_escape_string(trim($input['nama_kepala_keluarga'] ?? '')); $nik = $conn->real_escape_string($input['nik'] ?? ''); $alamat = $conn->real_escape_string($input['alamat'] ?? ''); $kelurahan = $conn->real_escape_string($input['kelurahan'] ?? ''); $kecamatan = $conn->real_escape_string($input['kecamatan'] ?? ''); $kota = $conn->real_escape_string($input['kota'] ?? ''); $lat = (float)($input['latitude'] ?? 0); $lng = (float)($input['longitude'] ?? 0); $tanggungan = (int)($input['jumlah_tanggungan'] ?? 1); $penghasilan= (float)($input['penghasilan_bulanan'] ?? 0); $pekerjaan = $conn->real_escape_string($input['pekerjaan'] ?? ''); $stsPekerjaan = $conn->real_escape_string($input['status_pekerjaan'] ?? 'tidak_bekerja'); $luasVal = !empty($input['luas_rumah']) ? (float)$input['luas_rumah'] : 'NULL'; $lantai = $conn->real_escape_string($input['jenis_lantai'] ?? 'semen'); $dinding = $conn->real_escape_string($input['jenis_dinding'] ?? 'tembok'); $air = $conn->real_escape_string($input['sumber_air'] ?? 'sumur'); $tinggal = $conn->real_escape_string($input['status_tempat_tinggal'] ?? 'milik_sendiri'); $kategoriInput = $conn->real_escape_string($input['kategori_kemiskinan'] ?? 'miskin'); $isKatManual = ($kategoriInput !== 'sistem') ? 1 : 0; $kategori = ($isKatManual === 1) ? $kategoriInput : hitungKategoriKesejahteraan($penghasilan, $tanggungan, $tinggal, $air); $stsbansos = $conn->real_escape_string($input['status_bantuan'] ?? 'belum_terima'); $rekInput = $conn->real_escape_string($input['rekomendasi_bantuan'] ?? 'sistem'); $isManual = ($rekInput !== 'sistem') ? 1 : 0; $ket = $conn->real_escape_string($input['keterangan'] ?? ''); $dikelola = isset($input['dikelola_oleh_ibadah_id']) && $input['dikelola_oleh_ibadah_id'] !== '' ? (int)$input['dikelola_oleh_ibadah_id'] : 'NULL'; // Cek duplikat KK (kecuali diri sendiri) $cek = $conn->query("SELECT id FROM rumah_warga WHERE no_kk='$no_kk' AND id != $id"); if ($cek->num_rows > 0) { echo json_encode(['success' => false, 'message' => 'Nomor KK sudah digunakan data lain']); break; } // Update anggota jika ada if (!empty($input['anggota']) && is_array($input['anggota'])) { $conn->query("DELETE FROM anggota_keluarga WHERE rumah_warga_id=$id"); simpanAnggota($conn, $id, $input['anggota']); } // Hitung rekomendasi otomatis if ($isManual === 1) { $rek = $rekInput; } else { $rek = hitungRekomendasi($conn, $id, $penghasilan, $stsPekerjaan); } $sql = "UPDATE rumah_warga SET no_kk='$no_kk', nama_kepala_keluarga='$nama', nik='$nik', alamat='$alamat', kelurahan='$kelurahan', kecamatan='$kecamatan', kota='$kota', latitude=$lat, longitude=$lng, jumlah_tanggungan=$tanggungan, penghasilan_bulanan=$penghasilan, pekerjaan='$pekerjaan', status_pekerjaan='$stsPekerjaan', luas_rumah=$luasVal, jenis_lantai='$lantai', jenis_dinding='$dinding', sumber_air='$air', status_tempat_tinggal='$tinggal', kategori_kemiskinan='$kategori', is_kategori_manual=$isKatManual, status_bantuan='$stsbansos', rekomendasi_bantuan='$rek', is_rekomendasi_manual=$isManual, keterangan='$ket', dikelola_oleh_ibadah_id=$dikelola WHERE id=$id"; if ($conn->query($sql)) { echo json_encode(['success' => true, 'message' => 'Data berhasil diperbarui', 'rekomendasi' => $rek]); } else { echo json_encode(['success' => false, 'message' => 'Gagal memperbarui: ' . $conn->error]); } break; // ── DELETE ─────────────────────────────────────────────────────────────── case 'DELETE': // Membatasi akses: HANYA Admin yang boleh menghapus data warga requireAuth(['admin']); $id = (int)($_GET['id'] ?? 0); if (!$id) { $input = json_decode(file_get_contents('php://input'), true); $id = (int)($input['id'] ?? 0); } if ($conn->query("DELETE FROM rumah_warga WHERE id=$id")) { echo json_encode(['success' => true, 'message' => 'Data berhasil dihapus']); } else { echo json_encode(['success' => false, 'message' => 'Gagal menghapus: ' . $conn->error]); } break; default: http_response_code(405); echo json_encode(['error' => 'Method not allowed']); } $conn->close(); // ════════════════════════════════════════════════════════════════════════════ function simpanAnggota(mysqli $conn, int $wargaId, array $list): void { foreach ($list as $a) { $nama = $conn->real_escape_string(trim($a['nama'] ?? '')); if (!$nama) continue; $nik = $conn->real_escape_string($a['nik'] ?? ''); $tl = !empty($a['tanggal_lahir']) ? "'" . $conn->real_escape_string($a['tanggal_lahir']) . "'" : 'NULL'; $jk = $conn->real_escape_string($a['jenis_kelamin'] ?? 'laki_laki'); $pend = $conn->real_escape_string($a['pendidikan'] ?? 'sd'); $pek = $conn->real_escape_string($a['pekerjaan'] ?? ''); $stsBk = $conn->real_escape_string($a['status_bekerja'] ?? 'tidak_bekerja'); $kes = $conn->real_escape_string($a['kondisi_kesehatan'] ?? 'sehat'); $bisa = (int)($a['dapat_bekerja'] ?? 1); $conn->query("INSERT INTO anggota_keluarga (rumah_warga_id,nama,nik,tanggal_lahir,jenis_kelamin,pendidikan,pekerjaan,status_bekerja,kondisi_kesehatan,dapat_bekerja) VALUES ($wargaId,'$nama','$nik',$tl,'$jk','$pend','$pek','$stsBk','$kes',$bisa)"); } }