| name | awcms-mini-erp-extension-readiness |
| description | Konsumsi atau evolusikan kontrak kesiapan ekstensi ERP AWCMS-Mini (business transaction, posting request/result, period-lock, item/currency/UoM, inventory movement, reconciliation, reporting projection). Gunakan saat membangun modul ERP langsung di `src/modules/` template ini yang perlu berinteraksi dengan tenant/party/scope/dokumen/event/reporting base, atau saat menambah/mengubah kontrak `_shared/business-transaction-contract.ts`/`_shared/erp-reference-data-contract.ts`/`_shared/ports/period-lock-port.ts` di sini sendiri. Sesuai Issue |
AWCMS-Mini — Kesiapan Ekstensi ERP
Sumber kebenaran: docs/adr/0020-erp-extension-readiness-contracts.md
(keputusan arsitektural mengikat), docs/awcms-mini/erp-extension- contracts.md (referensi teknis sebelas keluarga kontrak — ownership/
versi/failure-semantics/privasi/contoh per kontrak),
src/modules/_shared/business-transaction-contract.ts,
src/modules/_shared/erp-reference-data-contract.ts,
src/modules/_shared/ports/period-lock-port.ts,
tests/fixtures/example-domain-modules/modules/ example-erp-extension/ (fixture referensi lengkap).
Base ini bukan ERP. Tidak ada chart of accounts/jurnal/general
ledger/valuasi inventori/sales-purchase-order/AR-AP/payroll/pajak di
sini, dan tidak akan pernah ada (ADR-0013 §1). Skill ini TIDAK
mengajarkan cara membangun logika akuntansi — ia mengajarkan cara
memakai (atau, bila Anda mengerjakan issue base sendiri, cara
mengevolusikan) kontrak NETRAL yang sebuah ekstensi ERP eksternal
implementasikan.
Kapan pakai skill ini
- Membangun modul ERP langsung di
src/modules/ template ini — baca
§Playbook konsumsi di bawah.
- Menambah keluarga kontrak baru ke base ini sendiri (jarang —
hanya bila ada issue base baru yang eksplisit memintanya) — baca
§Playbook evolusi kontrak base.
- Mengubah
PeriodLockPort/business-transaction-contract.ts/
erp-reference-data-contract.ts — baca §Invariant yang tidak boleh
dilonggarkan dulu.
Playbook konsumsi (membangun modul ERP langsung di src/modules/)
- Baca
docs/awcms-mini/erp-extension-contracts.md — tabel sebelas
kontrak, mana yang BARU (business transaction, posting event,
period-lock, item/currency/UoM/inventory/reconciliation) vs mana
yang MEMAKAI ULANG mekanisme Wave 2/3 yang sudah ada (party
directory, business-scope hierarchy, document numbering, reporting
projection) — jangan menduplikasi yang sudah ada.
- Daftarkan modul ERP Anda di registry base
src/modules/index.ts
(listModules()), langsung di src/modules/ template ini (ADR-0024 —
tidak ada lagi ApplicationModuleRegistry/repo turunan terpisah) —
modul ERP Anda dependencies ke Core base (tenant_admin,
identity_access) seperti modul domain lain, LALU
capabilities.consumes opsional ke party_directory
(profile_identity) dan/atau organization_hierarchy_resolution
(organization_structure) bila Anda butuh referensi party/scope —
lihat tests/fixtures/example-domain-modules/modules/ example-erp-extension/module.ts untuk contoh persis.
- Implementasikan
PeriodLockPort Anda sendiri (base tidak
menyediakan satu pun adapter berperilaku nyata — hanya
noPeriodLockAdapterConfigured, yang SELALU checked: false). Mesin
posting Anda WAJIB memperlakukan checked: false identik dengan
locked: true untuk operasi "post" — lihat tests/fixtures/ example-domain-modules/modules/example-erp-extension/ posting-engine.ts untuk pola fail-closed yang benar.
- Registrasikan tipe event Anda sendiri (
"<extension_key>.posting. requested"/"...result_recorded") di atas domain_event_runtime
(Issue #742) di build Anda sendiri — payload berbentuk
AccountingPostingRequestPayload/AccountingPostingResultPayload.
Base TIDAK PERNAH menginterpretasi totalDebit/totalCredit/
ledgerReference — semuanya decimal-as-string/opaque.
- Tegakkan idempotency per
requestId (invariant #4 ADR-0020) —
request yang sama dikirim ulang harus mengembalikan hasil yang
identik, tidak pernah posting ganda. Tidak cukup sendirian —
tegakkan JUGA uniqueness posted-state per (tenantId, transactionType, externalTransactionId) (invariant #3), independen requestId:
sebuah requestId BARU untuk transaksi bisnis yang SAMA tetap harus
ditolak sebagai duplikat, bukan diterima sebagai posting kedua yang
independen (temuan security-auditor Medium pada PR #789 — fixture
awal hanya deduplikasi per requestId).
- Tegakkan reversal-sebagai-transaksi-baru (invariant #2) — JANGAN
PERNAH mengubah/menimpa baris transaksi yang sudah
"posted"; sebuah
koreksi selalu request baru dengan reversalOfExternalTransactionId
yang mereferensikan externalTransactionId transaksi asli — BUKAN
PERNAH requestId (ruang ID yang berbeda). Resolusi target reversal
WAJIB ter-scope tenant/legal-entity TERAUTENTIKASI request reversal
(invariant #7) — verifikasi ulang tenantId/legalEntityScope
transaksi asli yang ter-resolve secara eksplisit, jangan hanya
mengandalkan struktur key index (temuan security-auditor High pada PR
#789 — fixture awal mengindeks lewat requestId, ruang ID yang
salah, dan tidak memverifikasi ulang tenant/legal-entity sama sekali
— lihat posting-engine.ts's implementasi yang sudah diperbaiki untuk
pola yang benar).
- Jika Anda ingin kontribusi
reporting projection (Issue #753):
descriptor Anda WAJIB menegakkan requiredPermission-nya sendiri di
endpoint pembacaan Anda — jangan hanya mendeklarasikan field ini
(lihat catatan "descriptor field terdokumentasi tapi tidak
ditegakkan" di erp-extension-contracts.md §11, pola yang berulang
di Wave 3 epic ini).
bun run modules:compose:check + bun run modules:composition:inventory:check untuk memverifikasi registry base
tetap komposisi yang valid setelah modul ERP Anda ditambahkan
langsung ke src/modules/index.ts.
Playbook evolusi kontrak base (menambah keluarga kontrak baru di sini)
Hanya lakukan ini bila ada issue base baru yang eksplisit meminta
kontrak tambahan (jangan menambah kontrak "untuk jaga-jaga").
- Tentukan apakah kontrak baru itu tipe data pasif (taruh di
_shared/erp-reference-data-contract.ts atau
_shared/business-transaction-contract.ts, tanpa method/behavior)
atau port berperilaku (file baru _shared/ports/<nama>-port.ts,
HARUS: nol import dari modul manapun, method async menerima
tx: Bun.SQL eksplisit sebagai parameter pertama, dan — bila
relevan — sebuah adapter default fail-closed seperti
noPeriodLockAdapterConfigured).
- Jangan duplikasi kontrak yang sudah dimiliki modul lain — cek
dulu apakah
party-directory-port.ts/business-scope-hierarchy- port.ts/document_infrastructure's numbering/ProjectionDescriptor
sudah mencakup kebutuhan Anda sebelum menulis kontrak baru (empat
dari sebelas kontrak issue #755 memakai ulang mekanisme yang sudah
ada, bukan kebetulan — periksa dulu sebelum membuat baru).
- Update
docs/awcms-mini/erp-extension-contracts.md's tabel + section
per-kontrak (ownership/versioning/failure-semantics/privasi/contoh)
— jangan biarkan kontrak baru tanpa entri di dokumen ini.
- Tambah/luaskan
tests/fixtures/example-domain-modules/modules/ example-erp-extension/ untuk membuktikan kontrak baru bisa
diimplementasikan nyata (bukan hanya tipe TypeScript yang belum
pernah dipakai) — pola yang sama posting-engine.ts/
period-lock-adapter.ts sudah tetapkan.
bun run typecheck && bun test tests/unit/erp-extension-contracts.test.ts tests/unit/module-composition-fixture.test.ts sebelum PR.
- Bila keputusannya mengikat lintas dokumen (arah dependensi baru,
invariant baru), update ADR-0020 — JANGAN hanya menambah kode tanpa
memperbarui keputusan arsitekturalnya (doc 21 §9 punya catatan
eksplisit: kontrak murni tanpa modul baru tetap butuh ADR, bukan
proposal template modul).
Invariant yang tidak boleh dilonggarkan
- Posted immutable — tidak ada fungsi apa pun di base yang boleh
"membantu" memperbarui field
status: "posted" sebuah
BusinessTransactionReference secara in-place.
- Fail-closed period-lock —
checked: false HARUS sama beratnya
dengan locked: true untuk "post". Jangan pernah menambah jalur
yang memperlakukan "tidak bisa memeriksa" sebagai "izinkan saja".
- Tenant tetap batas keamanan —
legalEntityScope/periodKey/dsb.
TIDAK PERNAH menjadi pengganti RLS/ABAC; period-lock dan business-
scope keduanya eksplisit didokumentasikan sebagai "bukan batas
identitas".
- Tidak ada hard-dependency ke
reference_data — historis, Issue #750
masih OPEN dengan temuan Critical yang belum diperbaiki saat kontrak ini
pertama ditulis (per ADR-0020 §Status saat itu); Issue #750 sudah CLOSED
dan reference_data/module.ts's status sekarang "active". Keputusan
desainnya tetap berlaku terlepas dari status historis itu:
ItemReference/CurrencyReference/UnitOfMeasureReference sengaja
independen dari sumber datanya. Jangan tambahkan import ke
reference_data dari _shared/erp-reference-data-contract.ts tanpa
memverifikasi ulang status keamanan modul itu terlebih dahulu.
Verifikasi
bun run typecheck
bun test tests/unit/erp-extension-contracts.test.ts tests/unit/module-composition-fixture.test.ts
bun run repo:inventory:check (bila jumlah test/file berubah)
bun run check penuh sebelum PR (docs-only + kontrak TypeScript +
fixture, tanpa migration/endpoint baru — jangan asumsikan
db:migrate/api:spec:check perlu berubah kecuali Anda benar-benar
menambah tabel/rute baru di base).