Like 0

Revision ac54742f1fd20092e49a4a9e0ced10c61565574a

HANDOVER-FE-transmittal-settings.md Raw

Handover FE — Transmittal Project Settings

Dua setting baru di level project (Projects.settings) yang memengaruhi perilaku transmittal:

  1. Blokir download form transmittal selama transmittal masih dalam proses review.
  2. Reminder deadline (dalam hari) ke checker/approver yang belum merespons, dikirim email oleh cron 08:00 WIB.

Backend sudah jalan di staging. FE perlu menambah 2 kontrol di form pengaturan project dan menangani satu error baru saat download.


1. Endpoint yang dipakai

Kebutuhan Endpoint Permission
Baca settings GET /api/projects/:id member project
Ubah settings PATCH /api/projects/:projectId base.project.update (owner/admin)
Request download form transmittal POST /api/projects/:projectId/reports/transmittal-form/:transmittalId/request-download transmittal.download.create

Tidak ada endpoint baru. Settings tetap dikirim lewat PATCH project yang sudah ada.

Versi API dikirim lewat header (custom versioning), bukan di path. Pakai base URL + header yang sudah dipakai web saat ini.


2. Bentuk data di settings

Settings adalah satu kolom JSON, bukan kolom per-setting. Grup baru: transmittal_settings.

{
  "transmittal_settings": {
    "disable_download_when_open": true,
    "deadline_reminder_days": 3
  }
}

Aturan:

  • disable_download_when_open — boolean, default false.
  • deadline_reminder_days — integer 0..365, default 0 (= mati). null diterima, artinya unset.
  • Dua-duanya opsional. Grup transmittal_settings opsional.
  • Key lain di dalam settings wajib dipertahankan. Backend memakai catchall, jadi key yang tidak dikenal tidak ditolak dan tidak dibuang.

Bentuk data produksi (penting)

Row lama menyimpan nilai yang sama dua kali: di dalam grup dan di top-level. Contoh nyata:

{
  "approver_revoke_time": 2,
  "approver_revoke_time_unit": "days",
  "transmittal_settings": { "approver_revoke_time": 2, "approver_revoke_time_unit": "days" }
}

Backend membaca nested dulu, lalu fallback ke key dengan nama sama di top-level. Jadi penulisan baru cukup ke dalam transmittal_settings. Duplikat flat lama boleh dibiarkan.


3. Jebakan: PATCH mengganti seluruh kolom settings

settings adalah JSON column dan di-update replace, bukan merge. Kalau FE mengirim:

{ "settings": { "transmittal_settings": { "disable_download_when_open": true } } }

maka seluruh key lain (basis, category, milestone_id, partner_company_id, closing_approver_id, priority, approver_revoke_time, dst.) hilang.

Kontrak untuk FE:

  • Ambil settings dari GET /projects/:id.
  • Gabungkan perubahan secara lokal: settings.transmittal_settings = { ...settings.transmittal_settings, ...perubahan }.
  • Kirim objek settings hasil gabungan secara utuh di PATCH.
  • Jangan mengirim transmittal_settings saja.

Contoh payload benar:

{
  "settings": {
    "basis": "Berdasarkan arahan PT Danantara Asset Management",
    "category": "CONSULTANT",
    "priority": "MEDIUM",
    "milestone_id": "bf083b10-53f8-492c-bc02-c731608d7a62",
    "partner_company_id": "12219e63-8762-4a7b-a8f4-70cb9705ffd8",
    "closing_approver_id": "33b51ecb-62c2-4ef7-af2a-fc55a00e75bb",
    "basis_of_implementation": "Berdasarkan arahan PT Danantara Asset Management",
    "transmittal_settings": {
      "disable_download_when_open": true,
      "deadline_reminder_days": 3
    }
  }
}

4. Setting A — disable_download_when_open

Label yang disarankan: "Blokir download form transmittal selama masih diproses" Tipe kontrol: toggle/switch Tersedia di: pengaturan project, hanya untuk owner/admin (base.project.update)

Perilaku backend saat true:

  • Download form transmittal (request-download) ditolak hanya untuk transmittal dengan status OPEN, IN_CHECKING, IN_APPROVAL (status "masih dalam review").
  • Semua status lain tetap bisa didownload: DRAFT, SCHEDULED, SENT, REJECTED, REJECTED_WITH_NOTES, REJECTED_RESUBMISSION, APPROVED, APPROVED_WITH_NOTES, ARCHIVE.
  • Transmittal yang sudah closed (is_closed / effective_is_closed) tetap bisa didownload, apa pun statusnya.
  • Download dokumen lain (file biasa, lampiran) tidak terpengaruh. Yang diblokir hanya form transmittal.

Error baru yang harus ditangani

HTTP 409
{
  "statusCode": 409,
  "error": "TRANSMITTAL_DOWNLOAD_LOCKED",
  "message": "Download is disabled while this transmittal is still open."
}

Penanganan FE:

  • Branch pada status === 409 && error === 'TRANSMITTAL_DOWNLOAD_LOCKED'.
  • Tampilkan pesan yang jelas, bukan toast generik "gagal download". Sarankan: "Download dinonaktifkan selama transmittal masih diproses. Hubungi admin project untuk mengubah pengaturan."
  • Jangan retry otomatis. Ini keputusan konfigurasi, bukan error sementara.

Catatan desain: gate ini bukan batas keamanan, hanya penguncian kenyamanan. Kalau pembacaan settings gagal, backend memilih mengizinkan download (fail-open). Jangan andalkan ini untuk kontrol akses.

Latensi efek

Settings di-cache 60 detik di service transmittal. Setelah toggle diubah, efeknya bisa terasa hingga ~1 menit. Kalau QA menguji "matikan lalu langsung download", tunggu atau ulangi setelah 60 detik.


5. Setting B — deadline_reminder_days

Label yang disarankan: "Kirim pengingat H-… sebelum deadline" Tipe kontrol: number input (integer), boleh 0 Tersedia di: pengaturan project, hanya untuk owner/admin

Semantik:

  • Nilai 0 = fitur mati (default).
  • Nilai N > 0 = kirim email pengingat ketika sisa waktu menuju deadline ≤ N hari. N juga mencakup transmittal yang sudah lewat deadline.
  • Cron berjalan setiap hari 08:00 WIB, hanya untuk transmittal berstatus OPEN / IN_CHECKING / IN_APPROVAL dan belum closed.
  • Satu email per pasangan (approval node, user, due date). Tidak ada email berulang tiap hari untuk deadline yang sama.
  • Penerima: semua checker dan approver pada node tersebut yang belum merespons. Kalau user sudah merespons sebelum email dikirim, email dibatalkan saat itu juga.
  • Kalau transmittal selesai/ditutup sebelum email dikirim, email dibatalkan.

Tidak ada UI tambahan selain input angka. Tidak ada endpoint untuk melihat riwayat reminder.

Saran validasi FE: integer, 0 <= N <= 365. Backend menolak di luar itu dengan 400.


6. Validasi & pesan error

PATCH project memvalidasi settings:

Field Aturan Contoh tidak valid
disable_download_when_open boolean "true" (string) → 400
deadline_reminder_days integer, 0..365, atau null -1, 366, 1.5 → 400
transmittal_settings object [], "on" → 400

Balasan 400 memakai format validasi Zod yang sudah dipakai endpoint project lain. Tidak ada kode error khusus.

Hindari menulis nilai di luar rentang lewat jalur lain (script, migrasi). Nilai rusak bisa membuat pembacaan settings gagal di service lain.


7. Checklist QA

  • Owner/admin bisa membuka dan menyimpan kedua setting; user biasa tidak melihat/mendapat 403.
  • Simpan toggle tanpa mengubah field lain → cek GET /projects/:id masih memuat basis, category, milestone_id, partner_company_id, closing_approver_id, priority.
  • deadline_reminder_days = 0 → tidak ada email pengingat.
  • disable_download_when_open = true → download form transmittal IN_APPROVAL menghasilkan 409 TRANSMITTAL_DOWNLOAD_LOCKED dengan pesan yang benar.
  • disable_download_when_open = true → download form transmittal APPROVED tetap berhasil.
  • disable_download_when_open = true → download dokumen/lampiran lain tidak terpengaruh.
  • Ubah toggle, tunggu ≤ 60 detik, ulangi uji download (cache).
  • Project lama dengan duplikat flat di settings tidak kehilangan key setelah disimpan.

8. Yang belum ada di scope ini

  • Belum ada UI di web. Semua kontrol di dokumen ini masih perlu dibuat.
  • Email pengingat memakai template sementara transmittal.deadline.reminder (product consdoc, shell consdoc). Desain final dari tim design belum masuk; variabel template sudah tetap, jadi penggantian desain tidak mengubah data yang dikirim.
  • Pengiriman email bersifat at-least-once, bukan exactly-once. Duplikat hanya mungkin terjadi jika proses mati tepat setelah provider email menerima pesan. Untuk keperluan UI ini tidak berpengaruh.
  • Belum ada notifikasi in-app untuk reminder; hanya email.

9. Catatan deploy (untuk koordinasi, bukan pekerjaan FE)

  • Migration baru: transmittal_reminder_outbox (modules_transmittal) dan email_queue.idempotency_key (module-notification). Jalankan prisma migrate deploy di kedua service.
  • Template transmittal.deadline.reminder#consdoc perlu di-seed di database notification sebelum cron pertama.
  • @module/contracts sudah di-bump ke c29b305 di modules_base, modules_transmittal, dan module-notification.

10. Referensi commit

Repo Commit
module-contracts c29b305 feat(contracts): add bulk project settings RPC and email idempotency key
modules_base 845d7df feat(projects): expose transmittal settings and a bulk settings read
modules_base bcd9d53 chore(contracts): bump @module/contracts to c29b305
modules_transmittal 7d4baa7 feat(transmittal): gate form downloads and remind unanswered checkers
modules_transmittal 10df93c chore(contracts): bump @module/contracts to c29b305
module-notification 399345d feat(email): dedupe template emails by caller idempotency key
module-notification 35e7836 chore(contracts): bump @module/contracts to c29b305