Files
UAS_WEBGIS_MichelleOlivia_D…/sig-kemiskinan/api/penduduk.php
T
2026-06-10 21:46:56 +07:00

308 lines
15 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// api/penduduk.php v2.0
// ============================================================
// CRUD API Penduduk Miskin Field diperluas
// ============================================================
require_once __DIR__ . '/../includes/config.php';
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') exit;
$method = $_SERVER['REQUEST_METHOD'];
$db = getDB();
// ── Query SELECT lengkap (dipakai di GET & setelah write) ──
$SELECT_SQL = "
SELECT
pm.*,
w.kelurahan, w.kecamatan, w.kota,
ri.nama AS nama_ibadah,
ri.jenis AS jenis_ibadah,
u.nama AS nama_surveyor,
v.nama AS nama_verifikator,
fp.file_path AS foto_utama,
(
SELECT GROUP_CONCAT(jb.kode ORDER BY jb.kode SEPARATOR ',')
FROM bantuan_penduduk bp
JOIN jenis_bantuan jb ON jb.id = bp.bantuan_id
WHERE bp.penduduk_id = pm.id AND bp.status = 'aktif'
) AS kode_bantuan,
(
SELECT COUNT(*) FROM bantuan_penduduk bp
WHERE bp.penduduk_id = pm.id AND bp.status = 'aktif'
) AS jumlah_bantuan
FROM penduduk_miskin pm
LEFT JOIN wilayah w ON w.id = pm.wilayah_id
LEFT JOIN rumah_ibadah ri ON ri.id = pm.rumah_ibadah_id
LEFT JOIN users u ON u.id = pm.surveyor_id
LEFT JOIN users v ON v.id = pm.verified_by
LEFT JOIN foto_rumah fp ON fp.penduduk_id = pm.id AND fp.is_primary = 1
";
try {
switch ($method) {
// ════════════════════════════════════════════════════════
// GET Ambil data penduduk
// ════════════════════════════════════════════════════════
case 'GET':
$id = $_GET['id'] ?? null;
$kelurahan = $_GET['kelurahan'] ?? null;
$kecamatan = $_GET['kecamatan'] ?? null;
$kategori = $_GET['kategori'] ?? null;
$bantuan = $_GET['bantuan'] ?? null; // 'ada'|'tidak'
$verifikasi = $_GET['verifikasi'] ?? null;
$surveyor = $_GET['surveyor_id']?? null;
$search = $_GET['search'] ?? null;
// GET single
if ($id) {
$stmt = $db->prepare($SELECT_SQL . " WHERE pm.id = ?");
$stmt->execute([$id]);
$row = $stmt->fetch();
if (!$row) jsonResponse(['success' => false, 'error' => 'Data tidak ditemukan'], 404);
// Ambil semua foto
$fotoStmt = $db->prepare("SELECT * FROM foto_rumah WHERE penduduk_id = ? ORDER BY is_primary DESC, created_at ASC");
$fotoStmt->execute([$id]);
$row['fotos'] = $fotoStmt->fetchAll();
// Ambil semua bantuan
$bantuanStmt = $db->prepare("
SELECT bp.*, jb.kode, jb.nama AS nama_bantuan, jb.warna_badge
FROM bantuan_penduduk bp
JOIN jenis_bantuan jb ON jb.id = bp.bantuan_id
WHERE bp.penduduk_id = ?
ORDER BY jb.kode
");
$bantuanStmt->execute([$id]);
$row['bantuans'] = $bantuanStmt->fetchAll();
jsonResponse(['success' => true, 'data' => $row]);
}
// GET list dengan filter
$sql = $SELECT_SQL . " WHERE 1=1";
$params = [];
if ($kelurahan) { $sql .= " AND w.kelurahan = ?"; $params[] = $kelurahan; }
if ($kecamatan) { $sql .= " AND w.kecamatan = ?"; $params[] = $kecamatan; }
if ($kategori) { $sql .= " AND pm.kategori_kemiskinan = ?"; $params[] = $kategori; }
if ($verifikasi) { $sql .= " AND pm.status_verifikasi = ?"; $params[] = $verifikasi; }
if ($surveyor) { $sql .= " AND pm.surveyor_id = ?"; $params[] = $surveyor; }
if ($search) { $sql .= " AND (pm.nama_kk LIKE ? OR pm.nik_kk LIKE ? OR pm.alamat LIKE ?)";
$params[] = "%$search%"; $params[] = "%$search%"; $params[] = "%$search%"; }
if ($bantuan === 'tidak') {
$sql .= " AND (SELECT COUNT(*) FROM bantuan_penduduk bp WHERE bp.penduduk_id = pm.id AND bp.status='aktif') = 0";
} elseif ($bantuan === 'ada') {
$sql .= " AND (SELECT COUNT(*) FROM bantuan_penduduk bp WHERE bp.penduduk_id = pm.id AND bp.status='aktif') > 0";
}
$sql .= " ORDER BY pm.nama_kk ASC";
$stmt = $db->prepare($sql);
$stmt->execute($params);
$rows = $stmt->fetchAll();
// Statistik ringkas
$stats = [
'total' => count($rows),
'total_jiwa' => array_sum(array_column($rows, 'jumlah_anggota')),
'penerima_bantuan' => count(array_filter($rows, fn($r) => $r['jumlah_bantuan'] > 0)),
'belum_verifikasi' => count(array_filter($rows, fn($r) => $r['status_verifikasi'] === 'belum_diverifikasi')),
];
jsonResponse(['success' => true, 'data' => $rows, 'stats' => $stats]);
break;
// ════════════════════════════════════════════════════════
// POST Tambah penduduk baru
// ════════════════════════════════════════════════════════
case 'POST':
$in = getInput();
// Validasi wajib
$required = ['nik_kk', 'nama_kk', 'lat', 'lng'];
foreach ($required as $f) {
if (empty($in[$f])) jsonResponse(['success' => false, 'error' => "Field '$f' wajib diisi"], 400);
}
// Cek NIK duplikat
$cek = $db->prepare("SELECT id FROM penduduk_miskin WHERE nik_kk = ?");
$cek->execute([trim($in['nik_kk'])]);
if ($cek->fetch()) jsonResponse(['success' => false, 'error' => 'NIK sudah terdaftar di sistem'], 409);
// Hitung kategori otomatis
$penghasilan = isset($in['penghasilan_bulanan']) && $in['penghasilan_bulanan'] !== ''
? (float)$in['penghasilan_bulanan'] : null;
$kategori = hitungKategori($penghasilan);
$wilayahId = isset($in['wilayah_id']) && $in['wilayah_id'] !== '' ? (int)$in['wilayah_id'] : null;
$surveyorId = isset($in['surveyor_id']) && $in['surveyor_id'] !== '' ? (int)$in['surveyor_id'] : null;
$statusPekerjaan = $in['status_pekerjaan'] ?? 'tidak_bekerja';
$allowedPekerjaan = ['tidak_bekerja','buruh_harian','petani','nelayan','pedagang','pegawai_swasta','wiraswasta','lainnya'];
if (!in_array($statusPekerjaan, $allowedPekerjaan)) {
$statusPekerjaan = 'lainnya';
}
$db->prepare("
INSERT INTO penduduk_miskin (
nik_kk, nama_kk, no_hp, alamat, wilayah_id, rt, rw, lat, lng,
jumlah_anggota, jumlah_anak_sekolah,
penghasilan_bulanan, sumber_penghasilan, status_pekerjaan,
kategori_kemiskinan, kondisi_rumah, kepemilikan_rumah,
punya_bpjs, jenis_bpjs, status_verifikasi, surveyor_id
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
")->execute([
trim($in['nik_kk']),
trim($in['nama_kk']),
$in['no_hp'] ?? null,
$in['alamat'] ?? null,
$wilayahId,
$in['rt'] ?? null,
$in['rw'] ?? null,
(float)$in['lat'],
(float)$in['lng'],
max(1, (int)($in['jumlah_anggota'] ?? 1)),
max(0, (int)($in['jumlah_anak_sekolah'] ?? 0)),
$penghasilan,
$in['sumber_penghasilan'] ?? null,
$statusPekerjaan,
$kategori,
$in['kondisi_rumah'] ?? 'tidak_layak',
$in['kepemilikan_rumah'] ?? 'milik_sendiri',
(int)($in['punya_bpjs'] ?? 0),
$in['jenis_bpjs'] ?? 'tidak_punya',
'belum_diverifikasi',
$surveyorId,
]);
$newId = (int)$db->lastInsertId();
assignRumahIbadah($newId);
// Catat riwayat & notifikasi tidak block jika gagal (misal user_id tidak ada)
try {
catatRiwayat($newId, (int)($surveyorId ?? 1), 'submit', 'Data baru dari survei');
} catch(\Exception $e) { /* abaikan error audit trail */ }
try {
$db->prepare("
INSERT INTO notifikasi (jenis, judul, pesan, penduduk_id)
VALUES ('belum_verifikasi', 'Data Baru Belum Diverifikasi', ?, ?)
")->execute(["Data KK {$in['nama_kk']} (NIK: {$in['nik_kk']}) menunggu verifikasi admin.", $newId]);
} catch(\Exception $e) { /* abaikan error notifikasi */ }
$stmt = $db->prepare($SELECT_SQL . " WHERE pm.id = ?");
$stmt->execute([$newId]);
jsonResponse(['success' => true, 'data' => $stmt->fetch(), 'message' => 'Data penduduk berhasil ditambahkan']);
break;
// ════════════════════════════════════════════════════════
// PUT Update data penduduk
// ════════════════════════════════════════════════════════
case 'PUT':
$in = getInput();
$id = (int)($in['id'] ?? $_GET['id'] ?? 0);
if (!$id) jsonResponse(['success' => false, 'error' => 'ID diperlukan'], 400);
// Ambil data lama untuk audit
$old = $db->prepare("SELECT * FROM penduduk_miskin WHERE id = ?");
$old->execute([$id]);
$dataLama = $old->fetch();
if (!$dataLama) jsonResponse(['success' => false, 'error' => 'Data tidak ditemukan'], 404);
// Hitung kategori ulang jika penghasilan berubah
$penghasilan = array_key_exists('penghasilan_bulanan', $in)
? (($in['penghasilan_bulanan'] !== '') ? (float)$in['penghasilan_bulanan'] : null)
: (float)$dataLama['penghasilan_bulanan'];
$kategori = hitungKategori($penghasilan);
$allowed = [
'nama_kk','no_hp','alamat','wilayah_id','rt','rw','lat','lng',
'jumlah_anggota','jumlah_anak_sekolah','penghasilan_bulanan',
'sumber_penghasilan','status_pekerjaan','kondisi_rumah',
'kepemilikan_rumah','punya_bpjs','jenis_bpjs',
'status_verifikasi','catatan_verifikasi'
];
$fields = []; $values = []; $posChanged = false;
foreach ($allowed as $f) {
if (!array_key_exists($f, $in)) continue;
$fields[] = "$f = ?";
if ($f === 'lat' || $f === 'lng') { $values[] = (float)$in[$f]; $posChanged = true; }
elseif ($f === 'wilayah_id') { $values[] = (isset($in[$f]) && $in[$f] !== '') ? (int)$in[$f] : null; }
elseif ($f === 'status_pekerjaan') {
$statusPekerjaan = $in[$f] ?? 'tidak_bekerja';
$allowedPekerjaan = ['tidak_bekerja','buruh_harian','petani','nelayan','pedagang','pegawai_swasta','wiraswasta','lainnya'];
$values[] = in_array($statusPekerjaan, $allowedPekerjaan) ? $statusPekerjaan : 'lainnya';
}
elseif (in_array($f, ['jumlah_anggota','jumlah_anak_sekolah','punya_bpjs'])) $values[] = (int)$in[$f];
elseif ($f === 'penghasilan_bulanan') $values[] = $penghasilan;
else $values[] = trim((string)$in[$f]);
}
// Update kategori otomatis
$fields[] = "kategori_kemiskinan = ?";
$values[] = $kategori;
// Jika diverifikasi, catat verifikator
if (isset($in['status_verifikasi']) && $in['status_verifikasi'] === 'terverifikasi') {
$fields[] = "verified_by = ?"; $values[] = $in['verified_by'] ?? 1;
$fields[] = "verified_at = NOW()";
}
if (empty($fields)) jsonResponse(['success' => false, 'error' => 'Tidak ada data untuk diupdate'], 400);
$values[] = $id;
$db->prepare("UPDATE penduduk_miskin SET " . implode(', ', $fields) . " WHERE id = ?")
->execute($values);
if ($posChanged) assignRumahIbadah($id);
$aksi = 'update';
if (isset($in['status_verifikasi'])) {
if ($in['status_verifikasi'] === 'terverifikasi') {
$aksi = 'verifikasi';
} elseif ($in['status_verifikasi'] === 'ditolak') {
$aksi = 'tolak';
}
}
try {
catatRiwayat($id, (int)($in['updated_by'] ?? 1), $aksi, $in['catatan_verifikasi'] ?? null, $dataLama);
} catch(\Exception $e) { /* abaikan error audit trail */ }
$stmt = $db->prepare($SELECT_SQL . " WHERE pm.id = ?");
$stmt->execute([$id]);
jsonResponse(['success' => true, 'data' => $stmt->fetch(), 'message' => 'Data berhasil diperbarui']);
break;
// ════════════════════════════════════════════════════════
// DELETE Hapus penduduk
// ════════════════════════════════════════════════════════
case 'DELETE':
$id = (int)($_GET['id'] ?? getInput()['id'] ?? 0);
if (!$id) jsonResponse(['success' => false, 'error' => 'ID diperlukan'], 400);
// Hapus foto fisik terlebih dahulu
$fotos = $db->prepare("SELECT file_path FROM foto_rumah WHERE penduduk_id = ?");
$fotos->execute([$id]);
foreach ($fotos->fetchAll() as $foto) {
$path = __DIR__ . '/../' . $foto['file_path'];
if (file_exists($path)) @unlink($path);
}
catatRiwayat($id, 1, 'hapus', 'Data dihapus oleh admin');
$db->prepare("DELETE FROM penduduk_miskin WHERE id = ?")->execute([$id]);
jsonResponse(['success' => true, 'message' => 'Data penduduk berhasil dihapus']);
break;
default:
jsonResponse(['success' => false, 'error' => 'Method tidak diizinkan'], 405);
}
} catch (\Throwable $e) {
jsonResponse(['success' => false, 'error' => 'Database error: ' . $e->getMessage()], 500);
}