308 lines
15 KiB
PHP
308 lines
15 KiB
PHP
<?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);
|
||
} |