Skip to content

[FEAT-INAPORT-UPDATE-MANIFEST-CONTAMINED] Integrasi Inaportnet Warta Kapal (Manifest Lanjut Domestik), Flag Spesifikasi Kapal, dan Port Waste Management System (PWMS) ​

Modul: Pengangkut | Tipe: Feature | Stage Saat Ini: DEVELOPMENT | Tanggal Dokumen: 2026-09-04


1. Informasi Tugas & Referensi Bisnis ​

  • Task ID / Nomor Referensi: FEAT-INAPORT-UPDATE-MANIFEST-CONTAMINED

  • Sumber Instruksi: Git Branch feat/inaportUpdate (Integrasi Inaportnet Warta Kapal & Port Waste Management System)

  • Dokumen Referensi / Lampiran: Tidak ada

  • Task / Dependensi Terkait (Cross-Service): Tidak ada

  • Modul Terkait: Pengangkut

  • Service / Repository: pengangkut-backend

  • PIC Pengembang: Hisyam Dzicky

  • Latar Belakang & Ringkasan Kebutuhan: Penyesuaian integrasi data Inaportnet pada modul Warta Kapal dan Item Pengangkut. Perubahan mencakup penambahan dukungan jenis kegiatan "lanjut domestik" (activityType: 3) pada manifest Warta Kapal keberangkatan, penambahan flag spesifikasi operasional kapal (is_crossover, is_fast_boat, is_unloading_waste), serta implementasi menyeluruh modul kargo limbah terkontaminasi PWMS (Port Waste Management System / Reception Facility) Annex 1–6 dengan tabel relasi vendor pengolah limbah (contamined_companies) dan multi-upload berkas lampiran PDF.

  • Ruang Lingkup & Rincian Sub-Pekerjaan (Technical Scope & Milestones):

    • [x] Penambahan 3 flag kapal (is_crossover, is_fast_boat, is_unloading_waste) pada skema Sequelize ships, query agregat JSON pada ship_models, ship.service.js, dan validasi Zod ship-identity.validation.js
    • [x] Perluasan validasi Zod postItemManifestSchema untuk menerima activityType: 3 (Lanjut) dan pembatasan aturan prefix service number (1020 keberangkatan vs 1010 kedatangan)
    • [x] Pembaruan agregasi data getWartaManifest di warta_kapal_controller.js untuk menyertakan blok respon manifest lanjut pada kontainer dan kargo
    • [x] Penambahan 13 kolom baru pada tabel pengangkut.items_contamined untuk operasional pembuangan limbah, contact person, penandaan Reception Facility, dan berkas lampiran
    • [x] Pembuatan tabel baru pengangkut.contamined_companies beserta model Sequelize dan service layer untuk mencatat vendor/perusahaan pengolah limbah
    • [x] Implementasi route V2 POST /v1/item-contamined, PUT /v1/item-contamined/:contaminedId, dan GET /item-contamined dengan dukungan parsing multi-file (req.files)
    • [x] Penguatan keamanan file upload helper (multer.js) menggunakan crypto.randomUUID() untuk menggantikan Math.random() (SonarQube Cryptography Hotspot) serta mitigasi kerentanan library fast-xml-parser

2. Deployment Lifecycle ​

#EnvironmentTanggal & WaktuPICStatusCatatan
1Development2026-02-11 10:59Hisyam DzickyDEPLOYEDBranch: development (Merged from feat/inaportUpdate)
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-inaport-updateHermansyah Efendi
2026-09-04DevelopmentInitial log created from backend context blockHisyam Dzicky

3. Analisis Teknis & Rincian Perubahan ​

  • Alur Logika & Bisnis:

    • Warta Kapal Manifest Lanjut Domestik: Fungsi getWartaManifest memanggil getManifestInaportHeader dengan parameter activity 3 untuk kontainer dan kargo secara paralel via Promise.all. Objek respon yang dikembalikan ke klien kini memuat simpul lanjut di dalam data.container dan data.cargo.

    • Aturan Validasi Warta (postItemManifestSchema): Validasi Zod superRefine memastikan nomor layanan keberangkatan (1020) hanya mengizinkan jenis kegiatan muat (2) dan lanjut (3), sedangkan kedatangan (1010) hanya mengizinkan bongkar (1) dan muat (2).

    • Flag Spesifikasi Kapal: Penambahan atribut operasional kapal (is_crossover, is_fast_boat, is_unloading_waste) pada model ships, skema Sequelize, agregasi JSON kapal pada ship_models, serta validasi identitas kapal.

    • Kargo Terkontaminasi / PWMS (Reception Facility): Handler postItemContaminedV2 dan putItemContaminedV2 mengelola transaksi database terpusat (db.transaction) untuk menyimpan data agregat limbah (Annex 1 s.d. Annex 6, sampah elektronik), berkas lampiran PDF, dan relasi multi-perusahaan pengolah via contaminedCompaniesService.bulkInsert.

    • Multi-File Upload & Hotfix req.files: Perbaikan penanganan req.files pada controller PUT memastikan seluruh dokumen fisik (surat_pernyataan, surat_penyerahan, catatan_pembuangan_minyak, catatan_pembuangan_sampah, lampiran_lainnya) terbaca dengan benar saat update data.

    • Hardening Keamanan & Vulnerability Mitigation: Penamaan berkas unggahan pada app/helpers/multer.js diubah menggunakan crypto.randomUUID() untuk mencegah prediktabilitas nama file dan menyelesaikan temuan SonarQube Cryptography Hotspot, serta upgrade versi library fast-xml-parser.

    • Daftar File yang Berubah:

      • app/controllers/warta_kapal_controller.js [Controller]
      • app/controllers/v2/contamined.controller.js [Controller]
      • app/controllers/item_controller.js [Controller]
      • app/controllers/v2/item.controller.js [Controller]
      • app/routers/contamined_route.js [Router]
      • app/services/ship.service.js [Service]
      • app/services/item-contamined.service.js [Service]
      • app/services/item-contamined_companies.service.js [Service - File Baru]
      • app/services/item-others.service.js [Service]
      • app/models/ship_models.js [Model]
      • app/helpers/item.js [Helper]
      • app/helpers/multer.js [Helper]
      • app/middlewares/request_logger.js [Middleware]
      • app/schemas/ships.schema.js [Schema]
      • app/schemas/item-contamined.schema.js [Schema]
      • app/schemas/item-contamined_companies.schema.js [Schema - File Baru]
      • app/validations/warta-kapal.validation.js [Validation]
      • app/validations/ship-identity.validation.js [Validation]
      • package.json & package-lock.json [Dependencies]
  • Perubahan Database (DDL / DML):

    Tidak ada migration file terdeteksi di repositori — DDL wajib dieksekusi secara manual pada database PostgreSQL target:

    sql
    -- 1. Penambahan 3 kolom flag spesifikasi kapal
    ALTER TABLE pengangkut.ships
        ADD COLUMN IF NOT EXISTS is_crossover BOOLEAN DEFAULT NULL,
        ADD COLUMN IF NOT EXISTS is_fast_boat BOOLEAN DEFAULT NULL,
        ADD COLUMN IF NOT EXISTS is_unloading_waste BOOLEAN DEFAULT NULL;
    
    -- 2. Penambahan 13 kolom baru pada tabel items_contamined
    ALTER TABLE pengangkut.items_contamined
        ADD COLUMN IF NOT EXISTS is_bongkar_limbah BOOLEAN DEFAULT NULL,
        ADD COLUMN IF NOT EXISTS pelabuhan_pembuangan VARCHAR(255) DEFAULT NULL,
        ADD COLUMN IF NOT EXISTS tanggal_pembuangan TIMESTAMPTZ DEFAULT NULL,
        ADD COLUMN IF NOT EXISTS pelabuhan_pembuangan_selanjutnya VARCHAR(255) DEFAULT NULL,
        ADD COLUMN IF NOT EXISTS pic_pwms VARCHAR(255) DEFAULT NULL,
        ADD COLUMN IF NOT EXISTS surat_pernyataan VARCHAR(255) DEFAULT NULL,
        ADD COLUMN IF NOT EXISTS surat_penyerahan VARCHAR(255) DEFAULT NULL,
        ADD COLUMN IF NOT EXISTS catatan_pembuangan_minyak VARCHAR(255) DEFAULT NULL,
        ADD COLUMN IF NOT EXISTS catatan_pembuangan_sampah VARCHAR(255) DEFAULT NULL,
        ADD COLUMN IF NOT EXISTS lampiran_lainnya VARCHAR(255) DEFAULT NULL,
        ADD COLUMN IF NOT EXISTS contact_person VARCHAR(255) DEFAULT NULL,
        ADD COLUMN IF NOT EXISTS no_contact_person VARCHAR(255) DEFAULT NULL,
        ADD COLUMN IF NOT EXISTS is_reception_facility BOOLEAN DEFAULT NULL;
    
    -- 3. Pembuatan tabel baru relasi vendor pengolah limbah
    CREATE TABLE IF NOT EXISTS pengangkut.contamined_companies (
        id_contamined_companies SERIAL PRIMARY KEY,
        contamined_id INTEGER NOT NULL,
        service_number VARCHAR(255) DEFAULT NULL,
        company_name VARCHAR(255) DEFAULT NULL,
        company_type VARCHAR(255) DEFAULT NULL,
        company_code VARCHAR(255) DEFAULT NULL,
        no_pmku VARCHAR(255) DEFAULT NULL,
        npwp_principal VARCHAR(255) DEFAULT NULL,
        created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
        updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
    );
    
    -- Index foreign key untuk relasi tabel
    CREATE INDEX IF NOT EXISTS idx_contamined_companies_contamined_id 
        ON pengangkut.contamined_companies(contamined_id);
  • Kontrak API & Response Payload:

    1. GET /warta-kapal/manifest/:serviceNumber ​

    • Deskripsi: Mengambil ringkasan data manifest Warta Kapal (Bongkar, Muat, dan Lanjut) untuk kontainer dan kargo
    • Autentikasi / Guard: Bearer Token (doIntrospection)
    • Request Header / Params:
      • Header: Authorization: Bearer <token>
      • Params: serviceNumber (contoh: 10202602110001)
    • Response Payload (200 OK):
      json
      {
        "code": 200,
        "status": "Success",
        "message": "Data Ditemukan!",
        "data": {
          "container": {
            "bongkar": { "detail": [] },
            "muat": { "detail": [] },
            "lanjut": { "detail": [] }
          },
          "cargo": {
            "bongkar": { "detail": [] },
            "muat": { "detail": [] },
            "lanjut": { "detail": [] }
          }
        }
      }

    2. POST /v1/item-contamined ​

    • Deskripsi: Menyimpan data permohonan kargo limbah/terkontaminasi PWMS beserta berkas lampiran dan vendor pengolah
    • Autentikasi / Guard: Bearer Token (doIntrospection) + validatePdfWithService
    • Content-Type: multipart/form-data
    • Request Body & File Parameters:
      • service_number: string (min 10)
      • is_bongkar_limbah: boolean
      • is_reception_facility: boolean
      • pic_pwms: string
      • contact_person: string
      • no_contact_person: string
      • pelabuhan_pembuangan: string
      • tanggal_pembuangan: ISO Date String
      • pelabuhan_pembuangan_selanjutnya: string
      • annex1_capacity_kg, annex1_bongkar_kg, annex1_simpan_kg, dst (Annex 1 s.d. 6)
      • companies: array JSON [{ company_type, company_name, company_code, no_pmku, npwp_principal }]
      • Files: attachment, surat_pernyataan, surat_penyerahan, catatan_pembuangan_minyak, catatan_pembuangan_sampah, lampiran_lainnya
    • Response Payload (200 OK):
      json
      {
        "status": "Success",
        "code": 200,
        "message": "Berhasil insert",
        "data": {
          "contamined_id": 1,
          "service_number": "101012345678"
        }
      }

    3. PUT /v1/item-contamined/:contaminedId ​

    • Deskripsi: Memperbarui data kargo limbah terkontaminasi PWMS dan memperbarui berkas lampiran fisik
    • Autentikasi / Guard: Bearer Token (doIntrospection) + validatePdfWithService
    • Content-Type: multipart/form-data
    • Path Parameter: contaminedId (integer)
    • Response Payload (200 OK):
      json
      {
        "status": "Success",
        "code": 200,
        "message": "Berhasil update"
      }

    4. GET /item-contamined?serviceNumber={serviceNumber} ​

    • Deskripsi: Mengambil detail kargo limbah terkontaminasi berdasarkan nomor layanan
    • Autentikasi / Guard: Inbound API Key (validateInboundApiKey) + logInaportWarta
    • Request Query: serviceNumber={serviceNumber}
    • Response Payload (200 OK):
      json
      {
        "code": 200,
        "status": "Success",
        "data": {}
      }

4. Bukti Teknis (Technical Proof & Audit Trail) ​

  • Repository: pengangkut-backend
  • Branch Utama: feat/inaportUpdate
  • Merge Request / PR: —

Daftar Commit Terkait (Multi-Commit Trail) ​

Commit IDBranchAuthorTanggal & JamRingkasan Perubahan
239b9121368e1bb0f71a675d2e448d3ba787baedfeat/inaportUpdateHisyam Dzicky2026-02-11 10:59fix: req files item contamined
499c379f8a7ea9869c2a95ec48629f22188a0153feat/inaportUpdateHisyam Dzicky2026-02-11 10:12fix: change pmws to pwms
8c2fd8bab862ecaa221f8660a6c4fc350b18c195feat/inaportUpdateHisyam Dzicky2026-02-09 08:27fix: function update item contamined
21fe5f41d60c35581e6b91510d587a877a9484affeat/inaportUpdateHisyam Dzicky2026-02-06 16:19fix: sonar cryptography
e4d9f496dbface4176fa8dcc60976c0a1e9c3227feat/inaportUpdateHisyam Dzicky2026-02-06 15:57fix: vulner fast-xml-parser
c4fd7cdd0d9e1846d57060727b5e681260eeb82efeat/inaportUpdateHisyam Dzicky2026-02-06 15:33feat: logic get post put item contamined
cb1a8274bbebd19496d033f8aac29966cd3efe98feat/inaportUpdateHisyam Dzicky2026-01-22 16:26feat: get flag ship, put flag ship, get lanjut domestik, post lanjut domestik

Cuplikan Kode / Query Migration ​

sql
-- 1. Penambahan 3 kolom flag spesifikasi kapal
ALTER TABLE pengangkut.ships
    ADD COLUMN IF NOT EXISTS is_crossover BOOLEAN DEFAULT NULL,
    ADD COLUMN IF NOT EXISTS is_fast_boat BOOLEAN DEFAULT NULL,
    ADD COLUMN IF NOT EXISTS is_unloading_waste BOOLEAN DEFAULT NULL;

-- 2. Penambahan 13 kolom baru pada tabel items_contamined
ALTER TABLE pengangkut.items_contamined
    ADD COLUMN IF NOT EXISTS is_bongkar_limbah BOOLEAN DEFAULT NULL,
    ADD COLUMN IF NOT EXISTS pelabuhan_pembuangan VARCHAR(255) DEFAULT NULL,
    ADD COLUMN IF NOT EXISTS tanggal_pembuangan TIMESTAMPTZ DEFAULT NULL,
    ADD COLUMN IF NOT EXISTS pelabuhan_pembuangan_selanjutnya VARCHAR(255) DEFAULT NULL,
    ADD COLUMN IF NOT EXISTS pic_pwms VARCHAR(255) DEFAULT NULL,
    ADD COLUMN IF NOT EXISTS surat_pernyataan VARCHAR(255) DEFAULT NULL,
    ADD COLUMN IF NOT EXISTS surat_penyerahan VARCHAR(255) DEFAULT NULL,
    ADD COLUMN IF NOT EXISTS catatan_pembuangan_minyak VARCHAR(255) DEFAULT NULL,
    ADD COLUMN IF NOT EXISTS catatan_pembuangan_sampah VARCHAR(255) DEFAULT NULL,
    ADD COLUMN IF NOT EXISTS lampiran_lainnya VARCHAR(255) DEFAULT NULL,
    ADD COLUMN IF NOT EXISTS contact_person VARCHAR(255) DEFAULT NULL,
    ADD COLUMN IF NOT EXISTS no_contact_person VARCHAR(255) DEFAULT NULL,
    ADD COLUMN IF NOT EXISTS is_reception_facility BOOLEAN DEFAULT NULL;

-- 3. Pembuatan tabel baru relasi vendor pengolah limbah
CREATE TABLE IF NOT EXISTS pengangkut.contamined_companies (
    id_contamined_companies SERIAL PRIMARY KEY,
    contamined_id INTEGER NOT NULL,
    service_number VARCHAR(255) DEFAULT NULL,
    company_name VARCHAR(255) DEFAULT NULL,
    company_type VARCHAR(255) DEFAULT NULL,
    company_code VARCHAR(255) DEFAULT NULL,
    no_pmku VARCHAR(255) DEFAULT NULL,
    npwp_principal VARCHAR(255) DEFAULT NULL,
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

-- Index foreign key untuk relasi tabel
CREATE INDEX IF NOT EXISTS idx_contamined_companies_contamined_id 
    ON pengangkut.contamined_companies(contamined_id);
javascript
// 1. Validasi Zod Warta Kapal Manifest Lanjut
// app/validations/warta-kapal.validation.js
export const postItemManifestSchema = z.object({
  serviceNumber: z.string().min(10),
  manifestType: z.enum(['cargo', 'container']),
  activityType: z.enum(['1', '2', '3']),
  file: z.object({}),
}).superRefine((data, ctx) => {
  const prefix = data.serviceNumber.slice(0, 4);
  if (prefix === '1020') {
    if (!['2', '3'].includes(data.activityType)) {
      ctx.addIssue({
        path: ['activityType'],
        message: 'Jenis Kegiatan Keberangkatan hanya boleh muat dan lanjut',
        code: z.ZodIssueCode.custom,
      });
    }
  }
  if (prefix === '1010') {
    if (!['1', '2'].includes(data.activityType)) {
      ctx.addIssue({
        path: ['activityType'],
        message: 'Jenis Kegiatan Kedatangan hanya boleh bongkar dan muat',
        code: z.ZodIssueCode.custom,
      });
    }
  }
});
javascript
// 2. Skema Sequelize Tabel Baru contamined_companies
// app/schemas/item-contamined_companies.schema.js
export const ContaminedCompanies = db.define(
  "contamined_companies",
  {
    id_contamined_companies: {
      type: DataTypes.INTEGER,
      primaryKey: true,
      autoIncrement: true,
      allowNull: false,
    },
    contamined_id: {
      type: DataTypes.INTEGER,
      allowNull: false,
    },
    service_number: {
      type: DataTypes.STRING,
      allowNull: true,
    },
    company_name: {
      type: DataTypes.STRING,
      allowNull: true,
    },
    company_type: {
      type: DataTypes.STRING,
      allowNull: true,
    },
    company_code: {
      type: DataTypes.STRING,
      allowNull: true,
    },
    no_pmku: {
      type: DataTypes.STRING,
      allowNull: true,
    },
    npwp_principal: {
      type: DataTypes.STRING,
      allowNull: true,
    },
    created_at: {
      type: DataTypes.DATE,
      allowNull: true,
      defaultValue: db.literal("CURRENT_TIMESTAMP"),
    },
    updated_at: {
      type: DataTypes.DATE,
      allowNull: true,
      defaultValue: db.literal("CURRENT_TIMESTAMP"),
    },
  },
  {
    schema: "pengangkut",
    timestamps: false,
    freezeTableName: true,
  }
);
javascript
// 3. Keamanan Penamaan Berkas Unggahan dengan crypto.randomUUID()
// app/helpers/multer.js
filename: (req, file, callback) => {
  try {
    const ext = path.extname(file.originalname);
    const baseName = path
      .basename(file.originalname, ext)
      .replace(/[^a-zA-Z0-9-_]/g, '');
    const unique = crypto.randomUUID();
    callback(null, `${baseName}-${unique}${ext}`);
  } catch (error) {
    return callback(new Error('Error when generating file name'));
  }
}

5. Bukti Verifikasi Pengujian (QA / Unit Test / API Verification) ​

A. Verifikasi Unit Test (Development Stage) ​

  • Test Suite File: Tidak ada unit test disertakan pada commit branch ini
  • Status Eksekusi CLI: SKIPPED
text
// Tidak ada unit test suite yang diikutsertakan pada branch feat/inaportUpdate

B. Panduan Pengujian QA & Verifikasi Staging (QA Playbook) ​

  • Cakupan / Surface Pengujian: Full E2E (UI + Backend) & API / Postman Only
  • Kebutuhan Akses / Role Akun: Akun Agen Pelayaran / PPJK terverifikasi, Bearer Token sesi aktif, dan data permohonan nomor layanan kedatangan (1010) & keberangkatan (1020).
  • Hasil Pengujian UAT: PENDING
  • Catatan QA: —
Skenario Pengujian QA (Test Scenarios) ​
  1. Skenario 1 — Happy Path Manifest Lanjut pada Keberangkatan (1020):

    • Pre-kondisi: Nomor permohonan berawalan 1020 dalam status draf Warta Kapal keberangkatan.
    • Trigger / Request: POST /warta-kapal/manifest dengan payload serviceNumber: "1020xxxx", activityType: "3", manifestType: "container", dan file manifest terlampir.
    • Ekspektasi Response: HTTP 200 OK. Saat query GET /warta-kapal/manifest/1020xxxx, data berada di node data.container.lanjut.
    • Verifikasi Database / Log: Record manifest tersimpan dengan activity_type = 3.
  2. Skenario 2 — Negative Test Validasi Kedatangan vs Manifest Lanjut:

    • Pre-kondisi: Nomor permohonan berawalan 1010 (Kedatangan).
    • Trigger / Request: POST /warta-kapal/manifest dengan payload activityType: "3".
    • Ekspektasi Response: HTTP 400 Bad Request dengan pesan: "Jenis Kegiatan Kedatangan hanya boleh bongkar dan muat".
    • Verifikasi Database / Log: Validasi Zod menolak request sebelum pemrosesan database.
  3. Skenario 3 — Happy Path Penyimpanan Kargo Limbah PWMS & Relasi Vendor:

    • Pre-kondisi: Database telah dieksekusi DDL items_contamined (13 kolom) dan tabel contamined_companies.
    • Trigger / Request: POST /v1/item-contamined dengan form-data lengkap (flag is_bongkar_limbah: true, is_reception_facility: true, berkas PDF surat_pernyataan, dan payload vendor companies).
    • Ekspektasi Response: HTTP 200 OK dengan pesan "Berhasil insert".
    • Verifikasi Database / Log:
      • Data baris baru masuk ke pengangkut.items_contamined dengan kolom pic_pwms dan flag terisi.
      • Data relasi tersimpan pada pengangkut.contamined_companies sesuai nilai contamined_id.
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