[FEAT-RFC-SINKARKES-SELF-ASSESSMENT-GEJALA-MENULAR] Penyesuaian Kuesioner Sanitasi Kapal, Penunjukan BUS, Self-Assessment, dan Master Referensi Gejala Menular Kemenkes Sinkarkes
Modul: Pengangkut | Tipe: Feature | Stage Saat Ini:
DEVELOPMENT| Tanggal Dokumen: 2026-09-04
1. Informasi Tugas & Referensi Bisnis
Task ID / Nomor Referensi:
FEAT-RFC-SINKARKES-SELF-ASSESSMENT-GEJALA-MENULARSumber Instruksi: Git Branch
feat/rfc-sinkarkes(RFC Standardisasi Karantina Kesehatan Kemenkes Sinkarkes)Dokumen Referensi / Lampiran: Tidak ada
Task / Dependensi Terkait (Cross-Service): Tidak ada
Modul Terkait:
PengangkutService / Repository:
pengangkut-backendPIC Pengembang: Hermansyah Efendi
Latar Belakang & Ringkasan Kebutuhan: Penyesuaian dan standardisasi integrasi data Karantina Kesehatan (Sinkarkes Kemenkes) pada modul Pengangkut COP (Certificate of Pratique). Perubahan mencakup penambahan kuesioner penilaian mandiri kesehatan kapal (Self Assessment) terkait riwayat kunjungan wilayah terjangkit WHO, pemeriksaan ulang sanitasi kapal beserta penunjukan Badan Usaha Sanitasi (BUS), pencatatan kasus penyakit dan kematian selama pelayaran, serta pengadaan tabel master referensi gejala penyakit menular (
ref_gejala_menular) untuk validasi dan pemetaan gejala kesehatan awak/penumpang pada dokumen MDH (Maritime Declaration of Health).Ruang Lingkup & Rincian Sub-Pekerjaan (Technical Scope & Milestones):
- [x] Pembuatan tabel master referensi baru
pengangkut.ref_gejala_menularbeserta endpoint API dan service layer pendukung - [x] Penambahan 14 kolom baru pada tabel
pengangkut.data_sinkarkesuntuk kuesioner sanitasi, penunjukan BUS, dan self-assessment pelayaran - [x] Implementasi validasi aturan bisnis pemeriksaan sanitasi kapal (kewajiban pengisian BUS dan berkas SK Penunjukan jika sanitasi ulang diperlukan, serta auto-cleanup berkas fisik jika opsi dibatalkan)
- [x] Implementasi utilitas mapping kode gejala penyakit menular (
extractTrueKeys&buildBooleanMap) untuk sinkronisasi format boolean map UI dengan array kode di MDH Kesehatan Maritim - [x] Penambahan skema validasi Zod (
FormSinkarkesSanitasiValidationSchemadanFormSinkarkesSelfAssessmentValidationSchema) padacop.validation.js - [x] Pembaruan model
cop_models.js(getMainDataSINKARKES) dengan relasiLEFT JOINkeurl_sinkarkesuntuk mengambil URL surat penunjukan BUS
- [x] Pembuatan tabel master referensi baru
2. Deployment Lifecycle
| # | Environment | Tanggal & Waktu | PIC | Status | Catatan |
|---|---|---|---|---|---|
| 1 | Development | 2026-02-04 17:21 | Hermansyah Efendi | DEPLOYED | Branch: development (Merged from feat/rfc-sinkarkes) |
| 2 | Staging (UAT) | — | — | PENDING | — |
| 3 | Production | — | — | PENDING | — |
Riwayat Update Log
| Tanggal | Environment | Perubahan | PIC |
|---|---|---|---|
| 2026-09-07 | Development | Ditunda dari rilis Staging v1.18.2 (MR !2080) karena menunggu kesiapan integrasi sistem K/L; kode dicadangkan di branch restore/feat-rfc-sinkarkes | Hermansyah Efendi |
| 2026-09-04 | Development | Initial log created from backend context block | Hermansyah Efendi |
3. Analisis Teknis & Rincian Perubahan
Alur Logika & Bisnis:
- Validasi Pemeriksaan Ulang Sanitasi & Penunjukan BUS:
- Jika pengguna memilih
sanitasiRecheckRequire === 'yes', sistem mewajibkan pengisianbusNamedan memeriksa ketersediaan berkas SK Penunjukan (bus_appointment_letter) di tabelpengangkut.url_sinkarkes. Jika salah satu kosong, sistem melempar error400 Bad Request. - Jika pengguna memilih
noatauyes_with_no_bus, sistem secara otomatis menghapus berkas fisik SK Penunjukan dari disk storage server (jika ada), menghapus record URL terkait di database, dan mereset nilaibusNamemenjadinull.
- Jika pengguna memilih
- Standardisasi Gejala Menular pada MDH (Maritime Declaration of Health):
- Nilai gejala menular pada payload request diterima dalam format map boolean:
{ "KODE_GEJALA": true, "KODE_LAIN": false }. - Helper
extractTrueKeysmengekstrak kode-kode yang bernilaitruedan memvalidasinya terhadap daftar master di tabelref_gejala_menular. Jika ditemukan kode tidak dikenal, request ditolak dengan pesan error validasi. - Saat pembacaan data (GET), helper
buildBooleanMapmenyusun kembali array kode database menjadi struktur map boolean lengkap untuk konsumsi antarmuka pengguna (UI).
- Nilai gejala menular pada payload request diterima dalam format map boolean:
- Penyimpanan Struktur Fleksibel (JSONB):
- Kasus
penyakit_lainnyadankematian_akibat_penyakitdisimpan sebagai array objek JSONB ([{ "penyakit": string, "jumlah": number }]), memungkinkan pelaporan multi-kasus penyakit dinamis tanpa memerlukan skema tabel relasional anak terpisah.
- Kasus
- Validasi Pemeriksaan Ulang Sanitasi & Penunjukan BUS:
Daftar File yang Berubah:
- Controller / API Route:
app/controllers/v2/cop.controller.js(Logika validasi BUS, upload surat penunjukan, dan validasi kode gejala menular MDH)app/controllers/cop_controller.js(Response mapping sanitasi dan self-assessment padagetFormSINKARKES)app/controllers/data_referensi_controller.js(Handler endpointgetRefGejalaMenular)app/routers/data_referensi_route.js(Registrasi routeGET /ref/gejala-menular)
- Service / Business Logic:
app/services/ref-gejala-menular.service.js(File Baru: CRUD dan auto-numbering urut tabel master gejala menular)app/services/data-sinkarkes.service.js(Penyesuaian persistensi kolom sanitasi & self-assessment)app/services/cop.service.js(Definisi konstanta nama file dokumenurlSinkarkesfileName.busAppointmentLetter)app/services/kesehatan-maritim-mdh.service.js(Refactor mapping kode gejala menular)app/models/cop_models.js(Penyesuaian querygetMainDataSINKARKESdengan JOIN tabel berkas URL)
- Validation / Schema:
app/schemas/ref-gejala-menular.js(File Baru: Skema model Sequelize tabelpengangkut.ref_gejala_menular)app/schemas/data-sinkarkes.schema.js(Penambahan 14 field baru sanitasi & self-assessment)app/validations/cop.validation.js(Skema validasi Zod form sanitasi dan self-assessment)app/helpers/zod.js(Helper validatoroptionalEnumSchemadanisStartBeforeEnd)app/helpers/object.js(Helper konversiextractTrueKeysdanbuildBooleanMap)
- Controller / API Route:
Perubahan Database (DDL / DML):
Tidak ada migration file terdeteksi di repositori — DDL wajib dieksekusi secara manual pada database PostgreSQL target:
sql-- 1. Buat tipe ENUM untuk pilihan pemeriksaan ulang sanitasi kapal DO $$ BEGIN CREATE TYPE pengangkut.enum_data_sinkarkes_sanitasi_recheck_require AS ENUM ('no', 'yes', 'yes_with_no_bus'); EXCEPTION WHEN duplicate_object THEN NULL; END $$; -- 2. Penambahan 14 kolom baru pada tabel data_sinkarkes ALTER TABLE pengangkut.data_sinkarkes ADD COLUMN IF NOT EXISTS sanitasi_recheck_require pengangkut.enum_data_sinkarkes_sanitasi_recheck_require DEFAULT 'no', ADD COLUMN IF NOT EXISTS bus_name VARCHAR(255) DEFAULT NULL, ADD COLUMN IF NOT EXISTS visit_who_affected_area BOOLEAN DEFAULT FALSE, ADD COLUMN IF NOT EXISTS petugas_kesehatan BOOLEAN DEFAULT FALSE, ADD COLUMN IF NOT EXISTS jml_penumpang_turun INTEGER DEFAULT NULL, ADD COLUMN IF NOT EXISTS pelabuhan_berangkat VARCHAR(100) DEFAULT NULL, ADD COLUMN IF NOT EXISTS pelabuhan_singgah VARCHAR(100) DEFAULT NULL, ADD COLUMN IF NOT EXISTS pelabuhan_singgah_area VARCHAR(20) DEFAULT 'domestik', ADD COLUMN IF NOT EXISTS tanggal_singgah DATE DEFAULT NULL, ADD COLUMN IF NOT EXISTS penumpang_gejala_menular BOOLEAN DEFAULT FALSE, ADD COLUMN IF NOT EXISTS penyakit_lainnya JSONB DEFAULT '[]'::jsonb, ADD COLUMN IF NOT EXISTS kematian_akibat_penyakit JSONB DEFAULT '[]'::jsonb, ADD COLUMN IF NOT EXISTS sakit_yg_turun BOOLEAN DEFAULT FALSE, ADD COLUMN IF NOT EXISTS hewan_terinfeksi VARCHAR(255) DEFAULT NULL; -- 3. Pembuatan tabel master referensi gejala menular CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; CREATE TABLE IF NOT EXISTS pengangkut.ref_gejala_menular ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), kode VARCHAR(50) NOT NULL UNIQUE, nama VARCHAR(150) NOT NULL UNIQUE, deskripsi TEXT, aktif BOOLEAN NOT NULL DEFAULT TRUE, no_urut INTEGER NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- Index tabel master CREATE INDEX IF NOT EXISTS idx_ref_gejala_menular_kode ON pengangkut.ref_gejala_menular(kode); CREATE INDEX IF NOT EXISTS idx_ref_gejala_menular_no_urut ON pengangkut.ref_gejala_menular(no_urut);Kontrak API & Response Payload:
GET/ref/gejala-menular- Deskripsi: Mengambil daftar referensi gejala penyakit menular yang aktif untuk kebutuhan pemetaan form MDH.
- Autentikasi / Guard:
Bearer Token(doIntrospection) - Request Header / Query / Params:
Header:Authorization: Bearer <token>
- Response Payload (
200 OK):json{ "status": "Success", "code": 200, "message": "Data berhasil ditemukan", "data": [ { "id": "c1f7b0e1-4c12-4a7b-8911-7362a87a2210", "kode": "DEMAM", "nama": "Demam / Panas Tinggi", "deskripsi": "Suhu tubuh > 38 derajat Celcius", "aktif": true, "no_urut": 1 } ] } - Response Error (
401 Unauthorized/500 Internal Server Error):json{ "status": "Error", "code": 401, "message": "Unauthorized", "data": null }
POST/v2/data-cop- Deskripsi: Menyimpan data permohonan Sinkarkes lengkap dengan blok sanitasi kapal, penunjukan BUS, dan kuesioner self-assessment pelayaran.
- Autentikasi / Guard:
Bearer Token(doIntrospection) - Request Header / Query / Params:
Header:Authorization: Bearer <token>,Content-Type: application/json
- Request Body:json
{ "serviceNumber": "101012345678", "sanitasi": { "sanitasiRecheckRequire": "yes", "visitWhoAffectedArea": true, "busName": "PT Sanitasi Sejahtera Pelabuhan" }, "selfAssessment": { "petugasKesehatan": true, "jmlPenumpangTurun": 12, "pelabuhanBerangkat": "SGP", "pelabuhanSinggahArea": "internasional", "pelabuhanSinggah": "MYPKG", "tanggalSinggah": "2026-01-25", "penumpangGejalaMenular": true, "penyakitLainnya": [ { "penyakit": "Diare Akut", "jumlah": 2 } ], "kematianAkibatPenyakit": [], "sakitYgTurun": false, "hewanTerinfeksi": "Tikus / Rodentia", "kdProv": "31" }, "mdh": { "kesehatanMaritim": [ { "gejalaMenular": { "DEMAM": true, "BATUK": false } } ] } } - Response Payload (
200 OK):json{ "status": "Success", "code": 200, "message": "Berhasil menyimpan form SINKARKES", "data": null } - Response Error (
400 Bad Request):json{ "status": "Error", "code": 400, "message": "Pemeriksaan ulang untuk sanitasi kapal memerlukan pengisian BUS dan upload SK Penunjukan", "data": null }
4. Bukti Teknis (Technical Proof & Audit Trail)
- Repository:
pengangkut-backend - Branch Utama:
feat/rfc-sinkarkes - Merge Request / PR: — (Merged to
development)
Daftar Commit Terkait (Multi-Commit Trail)
| Commit ID | Branch | Author | Tanggal & Jam | Ringkasan Perubahan |
|---|---|---|---|---|
75c3cccee636752bb842382b5a400887f753bef5 | feat/rfc-sinkarkes | Hermansyah Efendi | 2026-02-04 17:21 | fix(sinkarkes): change data type hewan_terinfeksi to string |
10f0783e9a07f0fffd10969f7341fa5a70a743eb | feat/rfc-sinkarkes | Hermansyah Efendi | 2026-02-04 14:32 | fix: sanitasi sonarqube issue |
9bd8d0f104b4e7aebfd053e0412c78af2c63d55b | feat/rfc-sinkarkes | Hermansyah Efendi | 2026-02-04 13:48 | feat(sinkarkes): add zod validation for new payload |
004d3fce98d74796d462d1fa1827ca5c832bfa7d | feat/rfc-sinkarkes | Hermansyah Efendi | 2026-02-04 12:16 | add default value selfAssessment GET /data-cop |
54c68e49aa4846e9590bfd87bf03acf55f610272 | feat/rfc-sinkarkes | Hermansyah Efendi | 2026-02-04 11:54 | add kdProv payload /data-cop |
70166cbb51afc287f68d9711e513877eb6d21352 | feat/rfc-sinkarkes | Hermansyah Efendi | 2026-02-04 09:42 | remove unused payload mdh |
3264eec8da0272bab7fad9a3c69907d81d874425 | feat/rfc-sinkarkes | Hermansyah Efendi | 2026-02-03 14:09 | feat(schema): add new columns to data_sinkarkes |
2f05934158501644668b309e120d8e8785a312e5 | feat/rfc-sinkarkes | Hermansyah Efendi | 2026-02-03 09:03 | feat(sinkarkes): penambahakan kolom Self assessment dan gejala menular, penambahan table referensi gejala menular |
0f4bd3f3ba0ec594af316d742a96a53dc843926d | feat/rfc-sinkarkes | Hermansyah Efendi | 2026-01-29 16:16 | feat(sinkarkes): penambahan kolom dan pertanyaan di penangkutan |
Cuplikan Kode / Implementasi Inti
1. Validasi Guard Sanitasi, Kewajiban SK Penunjukan BUS, dan Auto-Cleanup Berkas Fisik
// app/controllers/v2/cop.controller.js
const checkAppointmentLetter = await UrlSinkarkes.findOne({
where: {
service_number: serviceNumber,
description: urlSinkarkesfileName.busAppointmentLetter
}
});
if (sanitasi?.sanitasiRecheckRequire === sanitasiRecheckRequireEnum.YES) {
if (!checkAppointmentLetter || !sanitasi?.busName) {
throw new BadRequestError('Pemeriksaan ulang untuk sanitasi kapal memerlukan pengisian BUS dan upload SK Penunjukan');
}
} else {
if (checkAppointmentLetter) {
const url = checkAppointmentLetter.dataValues.url;
if (url) {
const assetsIndex = url.indexOf('/assets');
if (assetsIndex !== -1) {
const relativePath = url.substring(assetsIndex).split('?')[0];
const fullPath = `${appRoot}${relativePath}`;
if (fs.existsSync(fullPath)) {
fs.unlinkSync(fullPath);
}
}
}
await checkAppointmentLetter.destroy();
}
sanitasi['busName'] = null;
}2. Skema Zod Form Sanitasi & Self-Assessment
// app/validations/cop.validation.js
const FormSinkarkesSanitasiValidationSchema = z.object({
sanitasiRecheckRequire: optionalEnumSchema(Object.values(sanitasiRecheckRequireEnum), 'sanitasiRecheckRequire'),
visitWhoAffectedArea: z.boolean().nullish(),
busName: z.string().nullish(),
}).nullish();
const FormSinkarkesSelfAssessmentValidationSchema = z.object({
petugasKesehatan: z.boolean().nullish(),
jmlPenumpangTurun: positiveNumberSchema('jmlPenumpangTurun'),
pelabuhanBerangkat: z.string().nullish(),
pelabuhanSinggahArea: optionalEnumSchema(['domestik', 'internasional'], 'pelabuhanSinggahArea'),
pelabuhanSinggah: z.string().nullish(),
tanggalSinggah: parseCustomDateWithOptional("tanggalSinggah"),
penumpangGejalaMenular: z.boolean().nullish(),
penyakitLainnya: z.array(z.object({
penyakit: z.string(),
jumlah: z.number(),
})).nullish(),
kematianAkibatPenyakit: z.array(z.object({
penyakit: z.string(),
jumlah: z.number(),
})).nullish(),
sakitYgTurun: z.boolean().nullish(),
hewanTerinfeksi: z.string().nullish(),
kdProv: z.string().nullish(),
}).nullish();5. Bukti Verifikasi Pengujian (QA / Unit Test / API Verification)
A. Verifikasi Unit Test (Development Stage)
(Opsional: Dilampirkan jika task mencakup penambahan/perubahan unit test)
- Test Suite File: Tidak ada unit test disertakan pada commit branch ini
- Status Eksekusi CLI:
SKIPPED
// Tidak ada unit test otomatis yang disertakan pada branch feat/rfc-sinkarkes.
// Verifikasi fungsi mengandalkan pengujian integrasi manual (Postman & UI).B. Panduan Pengujian QA & Verifikasi Staging (QA Playbook)
Diisi saat development sebagai panduan eksekusi pengujian QA, kemudian dilengkapi bukti response JSON saat verifikasi di Staging.
- Cakupan / Surface Pengujian:
Full E2E (UI + Backend) & API / Postman Only - Kebutuhan Akses / Role Akun: Agen Pelayaran terdaftar, Bearer Token aktif, permohonan COP aktif, dan berkas PDF SK Penunjukan BUS untuk pengujian upload.
- Hasil Pengujian UAT:
PENDING - Catatan QA: —
Skenario Pengujian QA (Test Scenarios)
Skenario 1 — Happy Path Pemeriksaan Ulang Sanitasi Kapal:
- Pre-kondisi: Berkas SK Penunjukan BUS telah diunggah ke
url_sinkarkesdengan kategoribus_appointment_letteruntukserviceNumbertarget. - Trigger / Request:
POST/v2/data-copdengan payloadsanitasi.sanitasiRecheckRequire: "yes"dansanitasi.busName: "PT Sanitasi Bahari". - Ekspektasi Response: HTTP
200 OK,message: "Berhasil menyimpan form SINKARKES". - Verifikasi Database / Log: Data tersimpan pada
pengangkut.data_sinkarkesdengansanitasi_recheck_require = 'yes'danbus_name = 'PT Sanitasi Bahari'.
- Pre-kondisi: Berkas SK Penunjukan BUS telah diunggah ke
Skenario 2 — Negative Case Pemeriksaan Ulang Sanitasi Tanpa Berkas SK Penunjukan:
- Pre-kondisi: Belum ada unggahan berkas SK Penunjukan BUS pada nomor layanan terkait.
- Trigger / Request:
POST/v2/data-copdengan payloadsanitasi.sanitasiRecheckRequire: "yes". - Ekspektasi Response: HTTP
400 Bad Request,message: "Pemeriksaan ulang untuk sanitasi kapal memerlukan pengisian BUS dan upload SK Penunjukan". - Verifikasi Database / Log: Tidak ada perubahan record pada tabel
pengangkut.data_sinkarkes.
Skenario 3 — Otomasi Pembersihan Berkas Saat Sanitasi Dibatalkan:
- Pre-kondisi: Berkas SK Penunjukan fisik ada di folder storage server dan tercatat pada tabel
pengangkut.url_sinkarkes. - Trigger / Request:
POST/v2/data-copdengan payloadsanitasi.sanitasiRecheckRequire: "no". - Ekspektasi Response: HTTP
200 OK,message: "Berhasil menyimpan form SINKARKES". - Verifikasi Database / Log: Record
bus_appointment_letterpadapengangkut.url_sinkarkesterhapus, berkas fisik di server disk terhapus, dan kolombus_namepadapengangkut.data_sinkarkesbernilainull.
- Pre-kondisi: Berkas SK Penunjukan fisik ada di folder storage server dan tercatat pada tabel
Bukti Response Verifikasi Staging (Diisi saat UAT)
{
"status": "PENDING",
"note": "Menunggu deployment ke environment Staging untuk eksekusi skenario uji QA."
}