Skip to content

[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-MENULAR

  • Sumber 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: Pengangkut

  • Service / Repository: pengangkut-backend

  • PIC 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_menular beserta endpoint API dan service layer pendukung
    • [x] Penambahan 14 kolom baru pada tabel pengangkut.data_sinkarkes untuk 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 (FormSinkarkesSanitasiValidationSchema dan FormSinkarkesSelfAssessmentValidationSchema) pada cop.validation.js
    • [x] Pembaruan model cop_models.js (getMainDataSINKARKES) dengan relasi LEFT JOIN ke url_sinkarkes untuk mengambil URL surat penunjukan BUS

2. Deployment Lifecycle ​

#EnvironmentTanggal & WaktuPICStatusCatatan
1Development2026-02-04 17:21Hermansyah EfendiDEPLOYEDBranch: development (Merged from feat/rfc-sinkarkes)
2Staging (UAT)——PENDING—
3Production——PENDING—

Riwayat Update Log ​

TanggalEnvironmentPerubahanPIC
2026-09-07DevelopmentDitunda dari rilis Staging v1.18.2 (MR !2080) karena menunggu kesiapan integrasi sistem K/L; kode dicadangkan di branch restore/feat-rfc-sinkarkesHermansyah Efendi
2026-09-04DevelopmentInitial log created from backend context blockHermansyah Efendi

3. Analisis Teknis & Rincian Perubahan ​

  • Alur Logika & Bisnis:

    • Validasi Pemeriksaan Ulang Sanitasi & Penunjukan BUS:
      • Jika pengguna memilih sanitasiRecheckRequire === 'yes', sistem mewajibkan pengisian busName dan memeriksa ketersediaan berkas SK Penunjukan (bus_appointment_letter) di tabel pengangkut.url_sinkarkes. Jika salah satu kosong, sistem melempar error 400 Bad Request.
      • Jika pengguna memilih no atau yes_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 nilai busName menjadi null.
    • 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 extractTrueKeys mengekstrak kode-kode yang bernilai true dan memvalidasinya terhadap daftar master di tabel ref_gejala_menular. Jika ditemukan kode tidak dikenal, request ditolak dengan pesan error validasi.
      • Saat pembacaan data (GET), helper buildBooleanMap menyusun kembali array kode database menjadi struktur map boolean lengkap untuk konsumsi antarmuka pengguna (UI).
    • Penyimpanan Struktur Fleksibel (JSONB):
      • Kasus penyakit_lainnya dan kematian_akibat_penyakit disimpan sebagai array objek JSONB ([{ "penyakit": string, "jumlah": number }]), memungkinkan pelaporan multi-kasus penyakit dinamis tanpa memerlukan skema tabel relasional anak terpisah.
  • 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 pada getFormSINKARKES)
      • app/controllers/data_referensi_controller.js (Handler endpoint getRefGejalaMenular)
      • app/routers/data_referensi_route.js (Registrasi route GET /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 dokumen urlSinkarkesfileName.busAppointmentLetter)
      • app/services/kesehatan-maritim-mdh.service.js (Refactor mapping kode gejala menular)
      • app/models/cop_models.js (Penyesuaian query getMainDataSINKARKES dengan JOIN tabel berkas URL)
    • Validation / Schema:
      • app/schemas/ref-gejala-menular.js (File Baru: Skema model Sequelize tabel pengangkut.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 validator optionalEnumSchema dan isStartBeforeEnd)
      • app/helpers/object.js (Helper konversi extractTrueKeys dan buildBooleanMap)
  • 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 IDBranchAuthorTanggal & JamRingkasan Perubahan
75c3cccee636752bb842382b5a400887f753bef5feat/rfc-sinkarkesHermansyah Efendi2026-02-04 17:21fix(sinkarkes): change data type hewan_terinfeksi to string
10f0783e9a07f0fffd10969f7341fa5a70a743ebfeat/rfc-sinkarkesHermansyah Efendi2026-02-04 14:32fix: sanitasi sonarqube issue
9bd8d0f104b4e7aebfd053e0412c78af2c63d55bfeat/rfc-sinkarkesHermansyah Efendi2026-02-04 13:48feat(sinkarkes): add zod validation for new payload
004d3fce98d74796d462d1fa1827ca5c832bfa7dfeat/rfc-sinkarkesHermansyah Efendi2026-02-04 12:16add default value selfAssessment GET /data-cop
54c68e49aa4846e9590bfd87bf03acf55f610272feat/rfc-sinkarkesHermansyah Efendi2026-02-04 11:54add kdProv payload /data-cop
70166cbb51afc287f68d9711e513877eb6d21352feat/rfc-sinkarkesHermansyah Efendi2026-02-04 09:42remove unused payload mdh
3264eec8da0272bab7fad9a3c69907d81d874425feat/rfc-sinkarkesHermansyah Efendi2026-02-03 14:09feat(schema): add new columns to data_sinkarkes
2f05934158501644668b309e120d8e8785a312e5feat/rfc-sinkarkesHermansyah Efendi2026-02-03 09:03feat(sinkarkes): penambahakan kolom Self assessment dan gejala menular, penambahan table referensi gejala menular
0f4bd3f3ba0ec594af316d742a96a53dc843926dfeat/rfc-sinkarkesHermansyah Efendi2026-01-29 16:16feat(sinkarkes): penambahan kolom dan pertanyaan di penangkutan

Cuplikan Kode / Implementasi Inti ​

1. Validasi Guard Sanitasi, Kewajiban SK Penunjukan BUS, dan Auto-Cleanup Berkas Fisik ​
javascript
// 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 ​
javascript
// 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
text
// 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) ​
  1. Skenario 1 — Happy Path Pemeriksaan Ulang Sanitasi Kapal:

    • Pre-kondisi: Berkas SK Penunjukan BUS telah diunggah ke url_sinkarkes dengan kategori bus_appointment_letter untuk serviceNumber target.
    • Trigger / Request: POST /v2/data-cop dengan payload sanitasi.sanitasiRecheckRequire: "yes" dan sanitasi.busName: "PT Sanitasi Bahari".
    • Ekspektasi Response: HTTP 200 OK, message: "Berhasil menyimpan form SINKARKES".
    • Verifikasi Database / Log: Data tersimpan pada pengangkut.data_sinkarkes dengan sanitasi_recheck_require = 'yes' dan bus_name = 'PT Sanitasi Bahari'.
  2. 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-cop dengan payload sanitasi.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.
  3. 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-cop dengan payload sanitasi.sanitasiRecheckRequire: "no".
    • Ekspektasi Response: HTTP 200 OK, message: "Berhasil menyimpan form SINKARKES".
    • Verifikasi Database / Log: Record bus_appointment_letter pada pengangkut.url_sinkarkes terhapus, berkas fisik di server disk terhapus, dan kolom bus_name pada pengangkut.data_sinkarkes bernilai null.
Bukti Response Verifikasi Staging (Diisi saat UAT) ​
json
{
  "status": "PENDING",
  "note": "Menunggu deployment ke environment Staging untuk eksekusi skenario uji QA."
}

Single Source of Truth (SSOT) Arsitektur & Perubahan Teknis Backend