Design Document · Approved 1 April 2026
Pipeline & Side Effects
Kapan stok berubah, kapan jurnal dibuat, dan siapa yang trigger.
Prinsip #1: 3 Track Status Independen
Status TIDAK boleh dicampur jadi 1 field. Setiap track punya lifecycle sendiri:
ORDER STATUS (business lifecycle):
dibuat → diproses → selesai / dibatalkan
FULFILLMENT STATUS (warehouse operation):
unfulfilled → picking → packed → shipped → delivered / returned
PAYMENT STATUS (money flow):
unpaid → settled → refunded
Contoh kombinasi yang valid:
Skenario Order Fulfillment Payment
Order baru masuk dibuat unfulfilled unpaid
Lagi di-pick di gudang diproses picking unpaid
Sudah dikirim, uang belum cair diproses shipped unpaid
Sampai ke customer, uang belum cair diproses delivered unpaid
Uang cair dari marketplace diproses delivered settled
Semua beres selesai delivered settled
Mengantar COD (bayar di tempat) diproses shipped paid
Dibatalkan sebelum kirim dibatalkan unfulfilled unpaid
Return setelah kirim diproses returned unpaid
Order "selesai" = derived. Bukan di-set manual. Otomatis selesai kalau fulfillment = delivered DAN payment = settled.
Prinsip #2: Side Effects = Reaksi dari Event
Event Trigger Side Effects
Order Masuk XLSX / API / manual Reserve stok
Ship Staff scan / API "shipped" / XLSX catch-up Potong stok (FEFO) + HPP + piutang + audit
Delivered Kurir confirm / API Update status only. Gak ada side effect keuangan.
Settlement XLSX rekon / API settlement Jurnal Dr.Kas / Cr.Piutang. Payment = settled.
Return API / XLSX / manual Reverse stok + reverse jurnal + klaim kurir
Cancel Internal / external Release reservation only (sebelum ship)
Prinsip #3: 3 Source, 1 Function
Semua trigger source masuk ke function yang SAMA . Gak ada logic terpisah per source.
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Staff Scan │ │ API Sync │ │ XLSX Import │
│ (real-time) │ │ (background)│ │ (catch-up) │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
└─────────────────┼─────────────────┘
▼
┌─────────────────────┐
│ process_ship_event()│
│ │
│ • Potong stok (FEFO)│
│ • HPP calculation │
│ • Create piutang │
│ • Audit trail │
│ • Journal (event dt)│
└─────────────────────┘
Masalah sekarang: Kalau staff gak scan (libur 3 hari), tapi marketplace sudah kirim 300 order → LabaBersih gak tau. Stok utuh, jurnal kosong, laporan SALAH. Dengan design ini, XLSX import atau API sync tetap trigger side effects yang sama.
Prinsip #4: Journal Date = Event Date
❌ SALAH:
TikTok shipped 28 Maret
Nelly upload XLSX 1 April
→ Jurnal tanggal 1 April
→ Laporan Maret: revenue KURANG
✅ BENAR:
TikTok shipped 28 Maret
Nelly upload XLSX 1 April
→ Jurnal tanggal 28 Maret
→ Laporan Maret: revenue BENAR
Semua journal date = shipped_at dari source (platform), BUKAN tanggal proses di LabaBersih.
Prinsip #5: Idempotent
Proses yang sama dipanggil 2x = aman. Skip, bukan error.
Skenario Behavior
Order sudah shipped → API sync bilang "shipped" lagi SKIP (already processed)
XLSX import order yang sudah ada SKIP (by nomor_pesanan)
Daily journal generate 2x hari sama SKIP (by batch_date + store_id)
Rekon order yang sudah settled REJECT (already reconciled)
Full Pipeline
ORDER MASUK (3 sources)
XLSX Import / API Sync / Manual Input
│
▼
┌─────────────────┐
│ CREATE ORDER │ → reserve stok
│ order: dibuat │
│ fulfillment: │
│ unfulfilled │
│ payment: unpaid │
└────────┬────────┘
│
▼
┌─────────────────┐
│ FULFILLMENT │ (internal: gudang kita)
│ picking → packed│
└────────┬────────┘
│
▼
┌─────────────────┐
│ SHIP EVENT │ → potong stok + HPP + piutang + audit
│ 3 trigger: │ journal date = shipped_at
│ • staff scan │
│ • API sync │ fulfillment: shipped
│ • XLSX catch-up │
└────────┬────────┘
│
▼
┌─────────────────┐
│ DELIVERED │ → update status only
│ trigger: API │ fulfillment: delivered
└────────┬────────┘
│
▼
┌─────────────────┐
│ SETTLEMENT │ → Dr.Kas / Cr.Piutang
│ trigger: │ payment: settled
│ • XLSX rekon │
│ • API settle │
└────────┬────────┘
│
▼
┌─────────────────┐
│ ORDER SELESAI │ ← auto: delivered + settled
│ order: selesai │
└─────────────────┘
BRANCHING:
┌─────────────────┐
│ RETURN / RTS │ → reverse stok + jurnal
│ fulfillment: │ klaim kurir kalau hilang
│ returned │
└─────────────────┘
┌─────────────────┐
│ CANCEL │ → release reservation only
│ (before ship) │ order: dibatalkan
└─────────────────┘
Execution Plan (7 Steps)
Step Apa Impact
1 Migration: split orders.status → order_status + fulfillment_status Schema change, data migration
2 Shared function: process_ship_event() 1 function, 3 trigger sources
3 XLSX import auto-process shipped orders Catch-up tanpa API
4 Update all callers (ship_shipment, ship_order, etc) Unified ship path
5 Update UI filters (fulfillment tabs, payment filter) UI reflects new model
6 Update all 268 tests Regression safety
7 Daily journal: use shipped_at date, not process date Correct accounting period
Status Mapping: Old → New
Old (1 field) Order Status Fulfillment Status Payment Status
dibuat dibuat unfulfilled unpaid
dikemas diproses unfulfilled unpaid
dikirim diproses shipped unpaid
selesai selesai delivered settled
rts diproses returned unpaid
dibatalkan dibatalkan unfulfilled unpaid
LabaBersih v2 — Pipeline Design · 1 April 2026