[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-CONTAMINEDSumber 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:
PengangkutService / Repository:
pengangkut-backendPIC 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 Sequelizeships, query agregat JSON padaship_models,ship.service.js, dan validasi Zodship-identity.validation.js - [x] Perluasan validasi Zod
postItemManifestSchemauntuk menerimaactivityType: 3(Lanjut) dan pembatasan aturan prefix service number (1020 keberangkatan vs 1010 kedatangan) - [x] Pembaruan agregasi data
getWartaManifestdiwarta_kapal_controller.jsuntuk menyertakan blok respon manifestlanjutpada kontainer dan kargo - [x] Penambahan 13 kolom baru pada tabel
pengangkut.items_contamineduntuk operasional pembuangan limbah, contact person, penandaan Reception Facility, dan berkas lampiran - [x] Pembuatan tabel baru
pengangkut.contamined_companiesbeserta model Sequelize dan service layer untuk mencatat vendor/perusahaan pengolah limbah - [x] Implementasi route V2
POST /v1/item-contamined,PUT /v1/item-contamined/:contaminedId, danGET /item-contamineddengan dukungan parsing multi-file (req.files) - [x] Penguatan keamanan file upload helper (
multer.js) menggunakancrypto.randomUUID()untuk menggantikanMath.random()(SonarQube Cryptography Hotspot) serta mitigasi kerentanan libraryfast-xml-parser
- [x] Penambahan 3 flag kapal (
2. Deployment Lifecycle
| # | Environment | Tanggal & Waktu | PIC | Status | Catatan |
|---|---|---|---|---|---|
| 1 | Development | 2026-02-11 10:59 | Hisyam Dzicky | DEPLOYED | Branch: development (Merged from feat/inaportUpdate) |
| 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-inaport-update | Hermansyah Efendi |
| 2026-09-04 | Development | Initial log created from backend context block | Hisyam Dzicky |
3. Analisis Teknis & Rincian Perubahan
Alur Logika & Bisnis:
Warta Kapal Manifest Lanjut Domestik: Fungsi
getWartaManifestmemanggilgetManifestInaportHeaderdengan parameter activity3untuk kontainer dan kargo secara paralel viaPromise.all. Objek respon yang dikembalikan ke klien kini memuat simpullanjutdi dalamdata.containerdandata.cargo.Aturan Validasi Warta (
postItemManifestSchema): Validasi ZodsuperRefinememastikan 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 modelships, skema Sequelize, agregasi JSON kapal padaship_models, serta validasi identitas kapal.Kargo Terkontaminasi / PWMS (Reception Facility): Handler
postItemContaminedV2danputItemContaminedV2mengelola 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 viacontaminedCompaniesService.bulkInsert.Multi-File Upload & Hotfix
req.files: Perbaikan penangananreq.filespada 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.jsdiubah menggunakancrypto.randomUUID()untuk mencegah prediktabilitas nama file dan menyelesaikan temuan SonarQube Cryptography Hotspot, serta upgrade versi libraryfast-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:booleanis_reception_facility:booleanpic_pwms:stringcontact_person:stringno_contact_person:stringpelabuhan_pembuangan:stringtanggal_pembuangan:ISO Date Stringpelabuhan_pembuangan_selanjutnya:stringannex1_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 ID | Branch | Author | Tanggal & Jam | Ringkasan Perubahan |
|---|---|---|---|---|
239b9121368e1bb0f71a675d2e448d3ba787baed | feat/inaportUpdate | Hisyam Dzicky | 2026-02-11 10:59 | fix: req files item contamined |
499c379f8a7ea9869c2a95ec48629f22188a0153 | feat/inaportUpdate | Hisyam Dzicky | 2026-02-11 10:12 | fix: change pmws to pwms |
8c2fd8bab862ecaa221f8660a6c4fc350b18c195 | feat/inaportUpdate | Hisyam Dzicky | 2026-02-09 08:27 | fix: function update item contamined |
21fe5f41d60c35581e6b91510d587a877a9484af | feat/inaportUpdate | Hisyam Dzicky | 2026-02-06 16:19 | fix: sonar cryptography |
e4d9f496dbface4176fa8dcc60976c0a1e9c3227 | feat/inaportUpdate | Hisyam Dzicky | 2026-02-06 15:57 | fix: vulner fast-xml-parser |
c4fd7cdd0d9e1846d57060727b5e681260eeb82e | feat/inaportUpdate | Hisyam Dzicky | 2026-02-06 15:33 | feat: logic get post put item contamined |
cb1a8274bbebd19496d033f8aac29966cd3efe98 | feat/inaportUpdate | Hisyam Dzicky | 2026-01-22 16:26 | feat: get flag ship, put flag ship, get lanjut domestik, post lanjut domestik |
Cuplikan Kode / Query Migration
-- 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);// 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,
});
}
}
});// 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,
}
);// 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
// Tidak ada unit test suite yang diikutsertakan pada branch feat/inaportUpdateB. 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)
Skenario 1 — Happy Path Manifest Lanjut pada Keberangkatan (1020):
- Pre-kondisi: Nomor permohonan berawalan
1020dalam status draf Warta Kapal keberangkatan. - Trigger / Request:
POST /warta-kapal/manifestdengan payloadserviceNumber: "1020xxxx",activityType: "3",manifestType: "container", dan file manifest terlampir. - Ekspektasi Response: HTTP
200 OK. Saat queryGET /warta-kapal/manifest/1020xxxx, data berada di nodedata.container.lanjut. - Verifikasi Database / Log: Record manifest tersimpan dengan
activity_type = 3.
- Pre-kondisi: Nomor permohonan berawalan
Skenario 2 — Negative Test Validasi Kedatangan vs Manifest Lanjut:
- Pre-kondisi: Nomor permohonan berawalan
1010(Kedatangan). - Trigger / Request:
POST /warta-kapal/manifestdengan payloadactivityType: "3". - Ekspektasi Response: HTTP
400 Bad Requestdengan pesan: "Jenis Kegiatan Kedatangan hanya boleh bongkar dan muat". - Verifikasi Database / Log: Validasi Zod menolak request sebelum pemrosesan database.
- Pre-kondisi: Nomor permohonan berawalan
Skenario 3 — Happy Path Penyimpanan Kargo Limbah PWMS & Relasi Vendor:
- Pre-kondisi: Database telah dieksekusi DDL
items_contamined(13 kolom) dan tabelcontamined_companies. - Trigger / Request:
POST /v1/item-contamineddengan form-data lengkap (flagis_bongkar_limbah: true,is_reception_facility: true, berkas PDFsurat_pernyataan, dan payload vendorcompanies). - Ekspektasi Response: HTTP
200 OKdengan pesan "Berhasil insert". - Verifikasi Database / Log:
- Data baris baru masuk ke
pengangkut.items_contamineddengan kolompic_pwmsdan flag terisi. - Data relasi tersimpan pada
pengangkut.contamined_companiessesuai nilaicontamined_id.
- Data baris baru masuk ke
- Pre-kondisi: Database telah dieksekusi DDL
Bukti Response Verifikasi Staging (Diisi saat UAT)
{
"status": "PENDING",
"note": "Menunggu deployment ke environment Staging untuk eksekusi skenario uji QA."
}