🔧 Bengkel Pintar ERP
Dokumentasi lengkap alur proses, diagram flow, dan approval workflow untuk seluruh modul sistem ERP manajemen bengkel otomotif multi-cabang.
Arsitektur Sistem
Gambaran besar sistem Bengkel Pintar ERP — stack teknologi, layar aplikasi, dan hubungan antar komponen utama.
🖥️ Tiga Lapisan Aplikasi
- 1bengkel-landing — Halaman marketing SaaS (Next.js, port 3001). Berisi fitur, harga, halaman demo, dan form permintaan demo yang terhubung ke backend via
POST /api/marketing/demo-request. - 2bengkel-admin — Aplikasi operasional utama (Next.js 15 App Router, port 4100). 130+ halaman, 5 portal role (main, kasir, sa, mekanik, operator). Semua request ke backend via
Authorization: Bearer {token}. Inline CSS, no Tailwind. - 3bengkel-pintar-api — Backend Hono + Bun (port 4000). 19 modul, 100+ endpoint. Autentikasi JWT, RBAC, multi-cabang, double-entry accounting.
⚙️ Stack Teknologi
| Layer | Teknologi |
|---|---|
| Runtime API | Bun + Hono (TypeScript) |
| Frontend | Next.js 15 App Router, React 19, Inline CSS (no Tailwind) |
| Database | PostgreSQL + Prisma ORM |
| File Storage | MinIO (S3-compatible, self-hosted) |
| Auth | JWT dual-token (access 15m + refresh 7d) |
| Generate SPK, Invoice A4, Struk Termal | |
| Notifikasi | WhatsApp (channel notifikasi pelanggan) |
🏢 Konsep Multi-Cabang
- Setiap data transaksi (WO, stok, karyawan) terikat ke branchId.
- Role admin dapat melihat dan mengelola semua cabang.
- Role manager hanya dapat mengakses data cabang sendiri (branch-scoped).
- Harga jasa dapat di-override per cabang via BranchJasaPrice.
- Stok dikelola per cabang via BranchStock, transfer antar cabang memerlukan approval.
- Laporan cabang dapat dibandingkan via endpoint
GET /api/dashboard(branchComparison).
🌐 Arsitektur Aplikasi & Integrasi Komponen
Next.js · Port 3001
SaaS Marketing Site"] ADMIN["bengkel-admin
Next.js 16 + React 19
Port 4100"] end subgraph API["⚙️ Backend API — bengkel-pintar-api"] direction TB HONO["Hono Framework
Bun Runtime · Port 4000"] AUTH_MW["Auth Middleware
JWT Verify + RBAC"] MODULES["19 Route Modules
workorder · quotation · kasir
inventory · hcm · finance · operator"] HONO --> AUTH_MW --> MODULES end subgraph DATA["🗄️ Data Layer"] PRISMA["Prisma ORM"] PG["PostgreSQL
Database"] MINIO["MinIO
Object Storage
Port 9000/9001"] PRISMA --> PG end subgraph INTEGRATIONS["🔗 Integrasi"] PDF["PDF Generator
SPK · Invoice · Receipt"] WA["WhatsApp
Notifikasi"] REPORT["Laporan
Balance Sheet · P&L"] end LANDING -->|"Demo Request API"| HONO ADMIN -->|"REST API + JWT"| HONO MODULES --> PRISMA MODULES --> MINIO MODULES --> PDF MODULES --> WA MODULES --> REPORT style CLIENT fill:#dbeafe,stroke:#3b82f6 style API fill:#ede9fe,stroke:#8b5cf6 style DATA fill:#d1fae5,stroke:#10b981 style INTEGRATIONS fill:#fef3c7,stroke:#f59e0b
👥 Role Pengguna & Akses Modul
Full Access"] MGR["📊 Manager
Branch Scoped"] SA["🎧 Service Advisor
WO + Quotation"] MKN["🔧 Mekanik
Job Execution"] KARU["👷 Karu (Kepala Regu)
Supervisi + QC"] KSR["💰 Kasir
Payment + Invoice"] GDG["📦 Gudang
Inventory + PO"] OPR["📋 Operator
Booking + Checkin"] end subgraph MODULES["Akses Modul"] M1["Work Order"] M2["Quotation"] M3["Kasir"] M4["Inventory"] M5["HCM"] M6["Finance"] M7["Booking"] M8["Master Data"] end ADM --> M1 & M2 & M3 & M4 & M5 & M6 & M7 & M8 MGR --> M1 & M2 & M4 & M5 & M6 & M7 SA --> M1 & M2 & M7 KARU --> M1 & M4 MKN --> M1 KSR --> M3 GDG --> M4 OPR --> M7 style ADM fill:#fee2e2,stroke:#ef4444 style MGR fill:#dbeafe,stroke:#3b82f6 style SA fill:#d1fae5,stroke:#10b981 style KARU fill:#fef9c3,stroke:#ca8a04 style MKN fill:#fef3c7,stroke:#f59e0b style KSR fill:#ede9fe,stroke:#8b5cf6 style GDG fill:#ccfbf1,stroke:#14b8a6 style OPR fill:#ffedd5,stroke:#f97316
Autentikasi & Manajemen User
Sistem login dengan JWT dual-token (access + refresh), reset password, dan manajemen user berbasis RBAC per cabang.
🔑 Mekanisme JWT Dual-Token
- 1User login → server mengembalikan accessToken (berlaku 15 menit) dan refreshToken (berlaku 7 hari).
- 2Setiap request API menyertakan
Authorization: Bearer {accessToken}di header. - 3Jika accessToken expired (401), frontend otomatis hit
POST /auth/refreshmenggunakan refreshToken untuk mendapat accessToken baru. - 4Jika refreshToken juga expired → user di-logout otomatis dan diredirect ke halaman login.
- !Login dibatasi 10 percobaan per 60 detik per IP (rate limiting) untuk mencegah brute force.
🔄 Redirect Berdasarkan Role (ROLE_REDIRECT)
| Role | Portal / Redirect Setelah Login |
|---|---|
| admin | /dashboard — portal utama (main) |
| manager | /dashboard — portal utama (main) |
| sa | /sa — portal Service Advisor |
| karu | /mekanik — portal mekanik (akses Kepala Regu) |
| mekanik | /mekanik — portal mekanik |
| kasir | /kasir — portal kasir |
| gudang | /dashboard — portal utama |
| operator | /operator — portal operator |
🛡️ Manajemen User oleh Admin
- Admin dapat membuat user baru dan menetapkan role serta cabang.
- Admin dapat toggle status user (aktif/nonaktif) tanpa menghapus data.
- Admin dapat mengatur allowedPages[] untuk memberikan akses ke halaman tertentu di luar role default.
- Admin dapat mengatur deniedPages[] untuk memblokir halaman tertentu meski role memiliki izin.
- Reset password dapat dilakukan admin tanpa mengetahui password lama user.
- Semua RefreshToken direvoke otomatis saat password direset.
🔗 Daftar Lengkap Auth Endpoints
| Method | Path | Keterangan |
|---|---|---|
| POST | /api/auth/login | Login, dapat accessToken + refreshToken |
| POST | /api/auth/refresh | Perbarui accessToken menggunakan refreshToken |
| POST | /api/auth/logout | Revoke refreshToken, akhiri sesi |
| GET | /api/auth/me | Data user yang sedang login |
| GET | /api/auth/my-permissions | Peta permission lengkap user (resource + action) |
| POST | /api/auth/ws-ticket | Issue tiket satu kali (30 detik) untuk upgrade WebSocket |
| PUT | /api/auth/password | Ganti password sendiri (perlu old password) |
| POST | /api/auth/forgot-password | Kirim link reset password ke email |
| POST | /api/auth/reset-password | Set password baru dengan token dari email |
🔑 Alur Login & Token Management
admin→/dashboard
sa→/sa
mekanik→/mekanik else Gagal API-->>FE: 401 Unauthorized FE-->>U: Tampilkan error end Note over FE,API: Setiap request API FE->>API: Header: Authorization Bearer accessToken API->>API: Verify JWT + check permissions alt Token expired (401) FE->>API: POST /api/auth/refresh API-->>FE: accessToken baru FE->>API: Retry original request else Refresh expired FE->>FE: Logout + redirect /login end Note over FE,API: Logout FE->>API: POST /api/auth/logout (refreshToken di body) API->>DB: Revoke RefreshToken API-->>FE: 200 OK Note over FE,API: WebSocket Auth FE->>API: POST /api/auth/ws-ticket API-->>FE: ticket (one-time, valid 30s) FE->>API: WS handshake ?ticket=xxx Note over API: Ticket ditukar jadi user context
🔄 Alur Reset Password
🛡️ Manajemen User & Permission (Admin)
📋 Struktur Data User & Token
Work Order (SPK — Surat Perintah Kerja)
Inti dari sistem operasional bengkel. Mengelola seluruh siklus servis kendaraan dari antrian masuk hingga kendaraan diserahkan ke pelanggan, termasuk penugasan mekanik, tracking pekerjaan per-item, dan pembayaran.
📋 6 Status Work Order
| Status | Artinya | Aktor |
|---|---|---|
| antrian | Kendaraan masuk, menunggu giliran diagnosa | SA / Sistem |
| diagnosa | SA dan mekanik memeriksa kendaraan, estimasi dibuat | SA |
| pengerjaan | Mekanik aktif mengerjakan job per item | Mekanik |
| quality_check | Pemeriksaan akhir hasil kerja sebelum diserahkan | Kepala Mekanik / SA |
| selesai | Kendaraan siap, menunggu pembayaran lunas | SA / Karu |
| diserahkan | Kendaraan sudah diserahkan ke pelanggan | SA |
💳 4 Status Pembayaran WO
| Status | Kondisi |
|---|---|
| belum | Belum ada pembayaran sama sekali |
| dp | Sudah bayar sebagian (down payment) |
| lunas | Pembayaran penuh, WO bisa diserahkan |
| tempo | Kredit — bayar belakangan sesuai kesepakatan |
WO hanya bisa berstatus diserahkan jika paymentStatus = lunas dan WO status = selesai.
⚙️ Job Tracking per Item
- Setiap WOItem (jasa atau parts) dapat memiliki satu WOJob yang di-assign ke mekanik tertentu.
- Mekanik dapat start → pause → resume → complete job dengan timer otomatis.
- Sistem mencatat estMinutes (estimasi) vs actualMinutes (aktual) untuk KPI.
- WO baru dapat masuk Quality Check jika semua WOJob sudah berstatus done.
- Mekanik dapat di-pause untuk request parts ke gudang, lalu resume setelah parts diterima.
👥 Aktor Terlibat
- SA — membuat SPK, assign mekanik, ubah status, serahkan kendaraan.
- Mekanik — mengambil job, tracking waktu, request parts.
- Kasir — menerima pembayaran, cetak invoice dan struk.
- Gudang — menyediakan parts yang di-request mekanik.
🔄 Lifecycle Work Order — Status Flow Lengkap
📝 Proses Pembuatan Work Order (SA Flow)
⚙️ Job Tracking — Alur Kerja Mekanik per Item
💳 Status Pembayaran Work Order
🗂️ Struktur Data Work Order (ERD)
Quotation / Estimasi Harga
Modul untuk membuat penawaran harga servis sebelum pekerjaan dimulai. Quotation yang disetujui pelanggan dapat langsung dikonversi menjadi Work Order.
📊 5 Status Quotation
| Status | Artinya |
|---|---|
| pending | Estimasi dibuat, menunggu konfirmasi pelanggan |
| approved | Pelanggan setuju, siap dikonversi ke SPK |
| rejected | Pelanggan menolak, penawaran tidak dilanjutkan |
| expired | Melewati tanggal validitas (validUntil) |
| converted | Sudah dikonversi menjadi Work Order aktif |
🔄 Proses Konversi ke SPK
- 1SA buat Quotation, tambahkan item jasa dan parts beserta harga.
- 2Quotation dikirim ke pelanggan via email, cetak, atau WhatsApp.
- 3Pelanggan bisa nego — SA update item/harga, lalu kirim ulang.
- 4Pelanggan setuju → status jadi approved.
- 5SA klik Convert → sistem buat WO baru, salin semua item, status WO: antrian.
- !Quotation yang sudah converted tidak bisa diubah lagi.
📋 Aturan Bisnis
- Quotation hanya bisa diedit selama berstatus pending.
- Tanggal validUntil diset saat pembuatan, sistem menandai expired otomatis setelah tanggal ini.
- Harga di Quotation tidak otomatis mengikuti perubahan harga master jasa — harga dikunci saat item ditambahkan.
- Setelah konversi, Quotation berstatus converted dan Work Order baru mendapat nomor SPK sendiri (
displayId). - Quotation yang rejected atau expired tidak dapat diaktifkan kembali, harus buat baru.
📊 Flow Status Quotation
🔄 Proses Quotation ke Work Order
Operator — Booking, Check-in & Follow-up
Modul front-desk untuk mengelola reservasi servis, penerimaan kendaraan, dan tindak lanjut pelanggan (servis berkala, pengingat STNK/asuransi, pelanggan tidak aktif).
📅 5 Status Booking
| Status | Artinya |
|---|---|
| scheduled | Booking dibuat, belum dikonfirmasi ke pelanggan |
| confirmed | Operator sudah konfirmasi jadwal ke pelanggan |
| arrived | Pelanggan sudah tiba, proses check-in dilakukan |
| no_show | Pelanggan tidak datang pada waktu yang dijadwalkan |
| cancelled | Booking dibatalkan oleh pelanggan atau operator |
📬 6 Tipe Follow-up Otomatis
| Tipe | Trigger |
|---|---|
servis_berkala | Km atau bulan interval servis sudah terlewati |
stnk_expired | Tanggal STNK kendaraan mendekati kadaluarsa |
insurance_expired | Asuransi kendaraan mendekati kadaluarsa |
inactive_customer | Pelanggan tidak servis lebih dari 3 bulan |
post_service | Survey kepuasan setelah servis selesai |
manual | Dibuat manual oleh operator |
🔄 Alur Status Follow-up
- 1PENDING — Follow-up masuk antrean operator.
- 2CONTACTED — Operator sudah menghubungi pelanggan via WA / Telepon.
- 3aSCHEDULED — Pelanggan mau booking servis, tanggal berikutnya dijadwalkan.
- 3bCOMPLETED — Pelanggan tidak perlu servis sekarang, follow-up selesai.
- 3cSKIPPED — Tidak relevan atau tidak bisa dihubungi, di-skip.
📅 Alur Booking & Penerimaan Kendaraan
📬 Alur Follow-up Pelanggan
Kasir & Pembayaran
Modul pengelolaan pembayaran, rekonsiliasi kas, dan pembuatan dokumen keuangan (invoice A4, struk termal). Mendukung multi-metode bayar dan sistem laci kas.
💳 5 Metode Pembayaran
| Metode | Keterangan |
|---|---|
| cash | Tunai — langsung dicatat ke laci kas aktif |
| transfer | Transfer bank — kasir input nomor referensi |
| debit_card | Kartu debit via mesin EDC |
| credit_card | Kartu kredit via mesin EDC |
| qris | Scan QR — terintegrasi via payment gateway |
🗄️ Laci Kas (Cash Drawer)
- 1Awal shift: kasir buka laci kas dan input saldo awal (opening balance).
- 2Setiap transaksi tunai otomatis tercatat: kas_masuk, kas_keluar, atau pembayaran_spk.
- 3Akhir shift: kasir tutup laci kas, input jumlah uang fisik yang dihitung.
- 4Sistem hitung selisih (difference) antara kas tercatat vs fisik untuk rekonsiliasi.
- !
Laci yang sudah closed tidak bisa dibuka ulang — harus buat laci baru.
📄 Dokumen yang Dicetak Kasir
| Dokumen | Format | Endpoint |
|---|---|---|
| Invoice Resmi | PDF A4 berlogo bengkel, detail item, total, PPn | GET /kasir/invoices/:id/pdf |
| Struk Kasir | PDF Thermal 80mm, ringkas untuk pelanggan | GET /kasir/invoices/:id/receipt |
| SPK / Work Order | PDF detail pekerjaan untuk mekanik | GET /api/spk/:id/pdf |
Invoice dan struk hanya bisa dicetak setelah WO berstatus selesai.
💳 Alur Pembayaran Work Order
🗄️ Manajemen Laci Kas (Cash Drawer)
🏦 Metode Pembayaran yang Didukung
Inventory & Manajemen Stok
Pengelolaan stok parts/sparepart per cabang, mutasi stok, transfer antar cabang, dan peringatan stok minimum. Setiap gerakan stok tercatat sebagai StockMovement.
📊 5 Tipe Stock Movement
| Tipe | Sumber | Efek |
|---|---|---|
masuk | PO diterima dari supplier | Stok bertambah (+qty) |
keluar | Pemakaian di Work Order / parts request | Stok berkurang (-qty) |
adjustment | Koreksi manual oleh gudang | Bisa +/- sesuai koreksi |
transfer_in | Menerima stok dari cabang lain | Stok bertambah (+qty) |
transfer_out | Mengirim stok ke cabang lain | Stok berkurang (-qty) |
⚠️ Low-Stock Alert
- Setiap part memiliki nilai minStock yang diset per cabang di
BranchStock. - Setiap kali stok bergerak, sistem cek: jika
stock <= minStock, part masuk daftar alert. - Alert tampil di Dashboard via endpoint
GET /api/parts/low-stock. - Gudang wajib menindaklanjuti dengan buat Purchase Request ke supplier.
- Setiap StockMovement menyimpan nilai stockAfter untuk audit trail historis.
🔄 Transfer Antar Cabang — 5 Status
| Status | Artinya |
|---|---|
| pending | Permintaan transfer dikirim, menunggu persetujuan cabang tujuan |
| approved | Manager cabang tujuan menyetujui transfer |
| in_transit | Barang sedang dalam perjalanan ke cabang tujuan |
| received | Barang diterima, stok kedua cabang diperbarui otomatis |
| rejected | Permintaan ditolak oleh cabang tujuan |
📊 Alur Pergerakan Stok (Stock Movement)
🔄 Transfer Stok Antar Cabang + Approval
Purchase Order & Purchase Request
Pengelolaan pembelian parts/sparepart ke supplier — dari permintaan pembelian (PR) oleh gudang/mekanik, approval, pembuatan PO, pengiriman, penerimaan, hingga pembayaran ke supplier.
📋 Purchase Request (PR)
- 1Gudang atau mekanik buat PR berisi daftar part yang dibutuhkan beserta alasan.
- 2PR masuk ke manager/admin untuk disetujui atau ditolak.
- 3PR yang disetujui dijadikan dasar pembuatan Purchase Order ke supplier.
- !PR yang ditolak harus dibuat ulang — tidak dapat diaktifkan kembali.
| Status PR | Artinya |
|---|---|
| pending | Menunggu persetujuan manager |
| approved | Disetujui, siap dibuatkan PO |
| rejected | Ditolak dengan catatan alasan |
| fulfilled | Barang sudah diterima dari supplier |
🛒 Purchase Order (PO) — 5 Status
| Status | Artinya | Bisa Diedit? |
|---|---|---|
| draft | Sedang disiapkan oleh gudang | Ya |
| dikirim | Sudah dikirim ke supplier | Tidak |
| diterima | Barang tiba, stok otomatis bertambah | Tidak |
| dibayar | Pembayaran ke supplier lunas | Tidak |
| batal | PO dibatalkan | Tidak |
📦 Integrasi dengan Stok
- Saat PO berstatus diterima via
POST /purchase-orders/:id/receive, sistem otomatis membuat StockMovement tipemasukuntuk setiap item PO. - BranchStock cabang yang menerima akan bertambah sejumlah qty yang diterima.
- Nilai stockAfter dicatat di setiap StockMovement untuk keperluan audit trail.
- Jika PO memiliki nominal besar, akan di-routing ke Approval Workflow sebelum bisa dikirim ke supplier.
🛒 Alur Purchase Request → Purchase Order
📊 Status Flow Purchase Order
Parts Request — Permintaan Parts dari Mekanik
Alur permintaan parts dari mekanik saat mengerjakan Work Order, approval oleh gudang, dan penyerahan fisik parts ke mekanik.
🔩 4 Status Parts Request
| Status | Artinya | Aktor |
|---|---|---|
| pending | Request dikirim, menunggu review gudang | Sistem |
| approved | Gudang menyetujui, sedang menyiapkan parts | Gudang |
| rejected | Stok tidak ada atau permintaan tidak valid | Gudang |
| delivered | Parts sudah diserahkan fisik ke mekanik, stok terpotong | Gudang |
📋 Aturan Bisnis
- Setiap request wajib mencantumkan referensi Work Order agar bisa di-tracking per SPK.
- Gudang memutuskan: jika stok ada → approve; jika tidak ada → reject dan buat PR ke supplier.
- Saat status berubah ke delivered, sistem otomatis membuat StockMovement tipe
keluar— stok cabang berkurang sejumlah qty yang diminta. - Mekanik bisa menjeda job (pause) sambil menunggu parts, lalu resume setelah parts diterima.
- Jika request ditolak, mekanik harus buat request baru dengan justifikasi alternatif.
🔩 Alur Parts Request
📋 Antrian Mekanik
HCM — Human Capital Management
Pengelolaan data karyawan, absensi harian, dan evaluasi performa mekanik melalui sistem KPI yang terukur (SPK selesai, on-time rate, return rate, customer rating, akurasi parts).
👤 Status Karyawan
| Status | Artinya |
|---|---|
| aktif | Karyawan aktif bekerja, bisa di-assign ke WO dan job |
| tidak_aktif | Sementara nonaktif (cuti panjang, dll) |
| resign | Karyawan sudah keluar — soft delete, data historis tetap tersimpan |
| Status Absensi | Keterangan |
|---|---|
| hadir | Check-in dan check-out tercatat normal |
| izin | Izin resmi dengan dokumen |
| sakit | Sakit dengan/tanpa surat dokter |
| alpha | Tidak hadir tanpa keterangan |
📊 5 Komponen KPI Mekanik
| Komponen | Cara Hitung | Bobot |
|---|---|---|
| SPK Done | Jumlah SPK selesai / target bulan | Produktivitas |
| On-Time Rate | % job selesai tepat waktu vs estimasi | Ketepatan |
| Return Rate | % SPK yang kembali karena keluhan (lebih rendah = lebih baik) | Kualitas |
| Customer Rating | Rata-rata rating dari pelanggan post-service | Kepuasan |
| Parts Accuracy | % request parts yang sesuai tanpa retur/lebih | Efisiensi |
Total KPI Score dipakai sebagai dasar penghitungan bonusKPI di modul Payroll. Satu record per mekanik per bulan (unique: empId + month).
🔩 Spesialisasi Mekanik
- Mesin / Engine — overhaul, tune up, timing belt, dll.
- AC Mobil — service AC, freon, kompresor.
- Kelistrikan — aki, alternator, kabel, ECU.
- Body & Cat — ketok, las, pengecatan.
- General — ganti oli, ban, rem, filter — semua mekanik bisa.
Spesialisasi dipakai SA saat assign mekanik ke WO Item untuk memastikan keahlian yang tepat.
👤 Manajemen Karyawan
⏰ Absensi — Check-in / Check-out
📊 KPI Mekanik — Komponen & Kalkulasi
Finance & Akuntansi
Sistem akuntansi double-entry lengkap dengan chart of accounts, jurnal, buku besar, periode fiskal, anggaran (budget), aset tetap, dan laporan keuangan otomatis (Balance Sheet, P&L, Trial Balance).
📒 Prinsip Double-Entry & Status Jurnal
- Setiap transaksi keuangan dibuat sebagai JournalEntry dengan minimal dua baris (JournalLine): satu sisi Debit, satu sisi Kredit.
- Sistem menolak jurnal jika total Debit ≠ total Kredit — wajib balance.
| Status | Artinya |
|---|---|
| draft | Jurnal masih bisa diedit, belum mempengaruhi saldo akun |
| posted | Jurnal dikunci, LedgerEntry dibuat, saldo akun diperbarui |
| voided | Jurnal dibatalkan — sistem buat entry reversal otomatis |
📅 Fiscal Period — Locking Periode
- 1Admin buat periode fiskal baru setiap bulan — status awal: OPEN.
- 2Selama periode OPEN, jurnal bisa diposting ke periode tersebut.
- 3Akhir bulan: admin tutup periode (
PATCH /periods/:id/close). - !Periode CLOSED tidak bisa dibuka kembali. Jurnal baru tidak dapat diposting ke periode yang sudah tutup — mencegah manipulasi laporan historis.
- 4Sistem otomatis buat closing entries — saldo Revenue dan Expense dipindah ke Retained Earnings.
📊 5 Tipe Akun (Chart of Accounts)
| Tipe | Contoh Akun | Laporan |
|---|---|---|
| asset | Kas, Bank, Piutang, Stok | Balance Sheet |
| liability | Hutang Usaha, Hutang Pajak | Balance Sheet |
| equity | Modal, Retained Earnings | Balance Sheet |
| revenue | Pendapatan Jasa, Pendapatan Parts | P&L / Income Statement |
| expense | HPP, Gaji, Sewa, Listrik | P&L / Income Statement |
Akun dengan flag isCashBank = true dipakai sebagai acuan laporan Arus Kas.
📈 Jurnal Otomatis dari Transaksi
- Pembayaran WO (Kasir) — Debit: Kas/Bank; Kredit: Pendapatan Jasa + Pendapatan Parts.
- Pembelian PO — Debit: Stok Parts; Kredit: Hutang Usaha / Kas.
- Penggajian (Payroll) — Debit: Beban Gaji; Kredit: Kas / Hutang Gaji.
- Penyesuaian Manual — Dibuat langsung oleh akuntan via modul Jurnal.
- Semua jurnal otomatis dibuat dengan status posted langsung (tanpa melalui draft).
📒 Alur Jurnal Akuntansi Double-Entry
📅 Manajemen Periode Fiskal
📈 Chart of Accounts & Laporan Keuangan
Aset Tetap & Penyusutan
Pencatatan aset tetap bengkel (lift, kompresor, kendaraan operasional, inventaris kantor), perhitungan penyusutan otomatis metode Garis Lurus (Straight-Line), serta pencatatan jurnal akuntansi yang dipicu sistem setiap awal bulan secara otomatis.
📐 Formula Penyusutan Garis Lurus
Harga Perolehan: Rp 45.000.000
Nilai Sisa: Rp 5.000.000
Masa Manfaat: 10 tahun (120 bulan)
Penyusutan = (45jt − 5jt) / 120 = Rp 333.333/bulan
📒 3 Akun Utama per Aset
| Field | Akun | Tipe |
|---|---|---|
accountId | Aset Tetap (1501–1504) | Asset |
accDepAccountId | Akumulasi Penyusutan (1502) | Asset contra |
depExpAccountId | Beban Penyusutan (6006) | Expense |
| Kode Akun | Nama |
|---|---|
| 1501 | Peralatan Bengkel |
| 1502 | Akumulasi Penyusutan |
| 1503 | Kendaraan Operasional |
| 1504 | Inventaris Kantor |
| 6006 | Beban Penyusutan |
| 4500 | Keuntungan Pelepasan Aset |
| 6500 | Kerugian Pelepasan Aset |
⚙️ Aturan Bisnis Penting
- Penyusutan berjalan otomatis via CRON setiap tanggal 1 untuk semua aset aktif (
isActive = true). - Penyusutan berhenti ketika
bookValue <= salvageValue— nilai buku tidak boleh turun di bawah nilai sisa. - Nilai buku (
bookValue) =purchaseCost − accumulatedDepreciation, diperbarui setiap bulan. - Setiap penyusutan bulanan otomatis membuat JournalEntry dengan tipe
depreciationdan statusposted. - Aset yang di-dispose di-set
isActive = falsedandisposedAtdiisi, penyusutan berhenti otomatis. - Disposal aset menghasilkan laba atau rugi pelepasan yang dicatat sebagai jurnal tersendiri.
🏭 Kategori Aset Bengkel
| Kategori | Contoh Aset | Masa Manfaat |
|---|---|---|
| Peralatan Bengkel | Lift hidrolik, kompresor, scanner ECU | 7–10 tahun |
| Kendaraan | Pick-up operasional, motor kurir | 5–8 tahun |
| Inventaris Kantor | Komputer, printer, meja kasir | 4–5 tahun |
| Bangunan | Gedung, pos satpam | 20–40 tahun |
🔄 Lifecycle Aset Tetap — Dari Pembelian hingga Disposal
📊 Simulasi Penyusutan Bulanan — Garis Lurus
💼 Jurnal Pelepasan Aset (Disposal) — Laba vs Rugi
🗂️ Struktur Data FixedAsset (ERD)
⏰ Alur Cron Penyusutan Otomatis (Tanggal 1 Setiap Bulan)
📈 Dampak Penyusutan ke Laporan Keuangan
Approval Workflow — Multi-Level Approval
Sistem persetujuan bertingkat yang fleksibel untuk berbagai modul (PO, PR, Payroll, Transfer Stok). Mendukung konfigurasi level approval, filter berdasarkan nominal, dan jenis approver (user/role).
🏗️ Konsep ApprovalRule
- module — modul yang diatur (PO, PR, Transfer, Payroll, Quotation).
- minAmount / maxAmount — rule berlaku hanya jika nominal transaksi berada di rentang ini. Contoh: rule khusus PO senilai Rp 10jt–50jt.
- applyToDraft — jika true, approval dibutuhkan bahkan sebelum dokumen di-finalize.
- isActive — rule bisa dinonaktifkan tanpa dihapus.
🔢 Level Approval & Tipe Persetujuan
| requireType | Artinya |
|---|---|
| any | Cukup satu approver di level ini yang menyetujui — request naik ke level berikutnya |
| all | Semua approver di level ini harus menyetujui sebelum naik ke level berikutnya |
- Approver bisa berupa user spesifik (userId) atau role (semua user dengan role tersebut bisa approve).
- Jika satu level di-reject, seluruh ApprovalRequest langsung ditolak — tidak perlu konfirmasi level lain.
- Request yang disetujui semua level → status approved, transaksi asal dapat dilanjutkan.
🔄 Cara Kerja Approval Check
- 1Sistem query
GET /api/approval/check?module=PO&amount=25000000untuk cek apakah transaksi perlu approval. - 2Jika perlu, sistem buat ApprovalRequest dan set
currentLevel = 1. - 3Approver Level 1 mendapat notifikasi, lalu submit keputusan via
POST /api/approval/decide. - 4Jika disetujui dan masih ada level berikutnya,
currentLevelnaik ke level 2. - 5Setelah semua level approve → ApprovalRequest berstatus approved, transaksi dilanjutkan.
🏗️ Konfigurasi Approval Rule
🔄 Alur Proses Approval Request
🗂️ ERD Sistem Approval
📋 Modul yang Menggunakan Approval
RBAC — Role-Based Access Control
Sistem izin akses berlapis: role sistem bawaan (admin, manager, sa, mekanik, kasir, gudang, operator) + custom dynamic role per cabang, dengan permission per resource dan override per user (allowedPages/deniedPages).
🛡️ 3 Lapisan Permission
- 1deniedPages[] — Pemblokiran per user. Jika route ada di sini, akses ditolak meski role punya izin. Dicek pertama kali.
- 2allowedPages[] — Pengecualian per user. Jika route ada di sini, akses diberikan meski role tidak punya izin. Dicek kedua.
- 3Role Permission — Default akses berdasarkan role sistem atau custom role. Dicek terakhir.
🔑 4 Operasi per Resource
| Operasi | HTTP Method | Contoh |
|---|---|---|
| canView | GET | Lihat daftar WO |
| canCreate | POST | Buat WO baru |
| canEdit | PUT / PATCH | Update status WO |
| canDelete | DELETE | Hapus item WO |
Permission diset per resource (contoh: workorder, inventory, finance) di RolePermission, baik untuk role sistem maupun custom role.
👥 7 Role Sistem (Built-in)
| Role | Scope | Akses Utama |
|---|---|---|
| admin | Semua cabang | Full — semua modul |
| manager | 1 cabang | Full — cabang sendiri |
| sa | 1 cabang | WO, Quotation, Customer |
| mekanik | 1 cabang | Job, Parts Request |
| kasir | 1 cabang | Pembayaran, Cash Drawer |
| gudang | 1 cabang | Inventory, PO, Transfer |
| operator | 1 cabang | Booking, Check-in, Follow-up |
🔐 Hirarki Permission Check
👥 Matrix Role × Modul Akses
Master Data
Data referensi yang menjadi pondasi seluruh transaksi — jasa bengkel, paket servis, parts, supplier, metode bayar, kendaraan, dan pengaturan per cabang.
📋 Hierarki Master Jasa
- 1Kategori Jasa — Pengelompokan layanan (Tune Up, AC, Rem, Kelistrikan, dll.).
- 2Master Jasa — Item layanan spesifik dengan harga dasar nasional dan estimasi waktu (
estMin). - 3BranchJasaPrice — Override harga per cabang. Jika ada, harga ini dipakai; jika tidak ada, pakai harga dasar.
- 4Service Package — Bundel jasa + parts dalam satu paket harga (misal: Paket Servis Berkala 10.000 km).
📦 Hierarki Master Parts
- 1Kategori Part — Pengelompokan (Filter, Oli, Kampas Rem, Busi, dll.).
- 2Master Part — Data global part: kode, SKU, harga beli, harga jual, brand, supplier default.
- 3BranchStock — Stok aktual per cabang +
minStockalert threshold per cabang.
⚙️ Setting Penting Per Cabang
| Setting | Konfigurasi |
|---|---|
| Penomoran | Prefix SPK (SPK-JKT-) dan Invoice (INV-JKT-) per cabang |
| Pajak | PPN default 11%, bisa dioverride per cabang |
| Metode Bayar | Aktifkan/nonaktifkan metode per cabang |
| Jam Operasional | Disimpan sebagai JSON (operHours) per hari |
| Bay / Lift | Status: available / occupied / maintenance |
| Department | Struktur organisasi per cabang (unique: branchId + name) |
🗂️ Peta Master Data
💰 Harga Jasa — Global vs Per-Cabang
Laporan & Dashboard
Pusat analitik real-time untuk monitoring operasional bengkel — dari SPK harian, utilitas mekanik, stok kritis, perbandingan cabang, hingga laporan keuangan lengkap.
📊 Dashboard Metrics — GET /api/dashboard
📑 Laporan yang Tersedia
End-to-End — Alur Sistem Lengkap
Gambaran alur transaksi dari pelanggan datang hingga laporan keuangan tercatat secara otomatis.
🔄 Alur Transaksi End-to-End Bengkel Pintar
Setup Awal & Prasyarat
Data master dan akun yang harus tersedia sebelum memulai pengujian.
👤 Akun Testing yang Dibutuhkan
| Role | Email Contoh | Akses Utama |
|---|---|---|
| admin | admin@bengkel.test | Semua modul |
| manager | manager@bengkel.test | Semua kecuali user management |
| sa | sa@bengkel.test | SPK, Booking, CRM, Quotation |
| mekanik | mekanik@bengkel.test | Antrian, Job, Parts request |
| kasir | kasir@bengkel.test | Pembayaran, Cash drawer |
| gudang | gudang@bengkel.test | Inventaris, PR approval |
| operator | operator@bengkel.test | Check-in, Booking, Status WO |
📦 Data Master yang Harus Ada
- Minimal 1 cabang aktif
- Minimal 1 Bay tersedia
- Minimal 1 data Jasa (service type)
- Minimal 1 data Part dengan stok > 0
- Minimal 1 Pelanggan + Kendaraan
- Metode Bayar: Tunai, Transfer, Kartu
- COA Finance sudah dikonfigurasi
Autentikasi & Manajemen User
Login, token refresh, reset password, buat user, nonaktifkan akun, rate limiting.
Login Valid — redirect sesuai role
- 1Buka
/login, masukkan email dan password valid - 2Klik Login
- Redirect sesuai role: admin/manager →
/dashboard, kasir →/kasir, mekanik →/mekanik/antrian - Token tersimpan di cookie/storage
- Header Topbar menampilkan nama dan initials user
Token Expired → Auto Refresh tanpa logout paksa
- 1Login, tunggu 15 menit (access token TTL)
- 2Buka halaman baru atau klik navigasi
Forgot Password — email link, token sekali pakai
- 1Buka
/forgot-password, masukkan email valid - 2Cek inbox email
- 3Klik link reset → isi password baru → submit
- Email berisi link reset (berlaku 1 jam)
- API tidak mengembalikan token di response (security)
- Token ditandai
used: truesetelah dipakai — tidak bisa dipakai ulang - Semua refresh token lama di-revoke → force re-login di device lain
Buat User Baru → welcome email + efek ke modul lain
- 1Admin → Settings → Users → Tambah User
- 2Isi nama, email, role, cabang, password sementara → Simpan
- Role
mekanik: user muncul di dropdown assign mekanik SPK - Role
kasir: user bisa membuka shift kasir
Nonaktifkan User → session di device lain mati
- 1User A sedang login di device lain
- 2Admin toggle inactive pada User A
/login.Rate Limiting Login — 429 setelah 10 percobaan
- 1Lakukan 11 kali percobaan login gagal dalam 60 detik
429 Too Many Requests.Golden Path End-to-End
Alur bisnis utama: Booking → Check-in → SPK → Mekanik → Parts → Kasir → Diserahkan. Melibatkan semua modul secara berurutan.
🔄 Urutan Alur
↓
OPERATOR → Check-in (buat SPK, assign bay)
↓
SA → Tambah item jasa + part, assign mekanik
↓
MEKANIK → Mulai → selesai job
↓
GUDANG → Approve parts request (jika perlu)
↓
SA/MANAGER → Quality check → selesai
↓
KASIR → Proses pembayaran lunas
↓
KASIR → Status → diserahkan
↓
[Efek: stok -1, jurnal keuangan, CRM update, bay free, audit log]
📋 TC-FLOW-01: Detail Step-by-Step
| # | Role | Aksi | Expected & Efek Samping |
|---|---|---|---|
| 1 | operator | Buat booking H-1, customer A, kendaraan B, jam 09:00 | Status = scheduled. Email konfirmasi terkirim. WS booking:created |
| 2 | operator | Customer datang → konfirmasi booking | Status = confirmed |
| 3 | operator | Check-in: pilih pelanggan, kendaraan, odometer, bay | SPK dibuat SPK-YYYYMM-XXXX, status = antrian. Bay = occupied. lastVisit diperbarui. WS wo:created + bay:occupied |
| 4 | sa | Tambah item: Tune Up Rp 150.000 + Filter Oli Rp 50.000 qty 1 | subtotal=200.000, PPn 11%=22.000, total=222.000. Job otomatis untuk item jasa |
| 5 | sa | Assign mekanik ke SPK | MechanicAssignment dibuat. Job.employeeId terisi |
| 6 | sa | Status → pengerjaan | Validasi: semua job harus ada mekanik. WS wo:status_changed |
| 7 | mekanik | Klik "Mulai" pada job Tune Up | Job status = running, startedAt terisi. WS wo:job_started |
| 8 | mekanik | Klik "Selesai" pada job | Job = done, actualMinutes dihitung. WS wo:job_done |
| 9 | sa | Status → quality_check | Validasi: semua job harus done. Error jika ada yang belum. WS wo:status_changed |
| 10 | sa | Status → selesai | WS wo:ready_to_pay → kasir. Email: Vehicle Ready + WhatsApp ke customer |
| 11 | kasir | Terima notifikasi → buka daftar antrian bayar | SPK muncul dengan total tagihan |
| 12 | kasir | Bayar Tunai Rp 222.000 | paymentStatus = lunas. Journal keuangan dibuat. WS wo:paid. Email: Invoice |
| 13 | kasir | Status → diserahkan | Validasi: harus lunas. totalSpend +222.000. Bay = available. WS bay:free |
| 14 | admin | Cek Audit Log | Semua aksi tercatat: create WO, status changes, payment, assignment |
| 15 | admin | Cek Finance → Jurnal | Journal entry otomatis dari payment muncul (Dr Kas / Cr Pendapatan) |
| 16 | admin | Cek Inventaris → Mutasi Stok | Stok Filter Oli berkurang 1. Movement tipe keluar dibuat |
| 17 | admin | Cek CRM → Pelanggan A | totalSpend bertambah, lastVisit diperbarui, riwayat servis muncul |
Work Order (SPK) — Detail
Validasi transisi status, kalkulasi total, blokir kondisi tidak valid, cetak PDF.
Transisi Status Tidak Valid
- 1Buat SPK status
antrian - 2Coba langsung ubah ke
diserahkan
Blokir quality_check jika Job Belum Selesai
- 1SPK status
pengerjaandengan 2 job - 2Mekanik selesaikan hanya 1 job
- 3SA ubah status →
quality_check
Blokir diserahkan jika Belum Lunas
- 1SPK status
selesai, paymentStatus =dp - 2Ubah status →
diserahkan
Hitung Ulang Total saat Item Berubah
| Aksi | Subtotal | PPn | Total |
|---|---|---|---|
| qty=2, price=100rb, disc=0% | 200.000 | 22.000 | 222.000 |
| Update disc=10% | 180.000 | 19.800 | 199.800 |
| + item baru 50rb | 230.000 | 25.300 | 255.300 |
| Hapus item pertama | 50.000 | 5.500 | 55.500 |
Status Flow yang Valid
| Dari | Ke (boleh) | Syarat |
|---|---|---|
| antrian | diagnosa | - |
| diagnosa | pengerjaan | Semua job punya mekanik |
| pengerjaan | quality_check | Semua job done |
| quality_check | selesai | - |
| selesai | diserahkan | paymentStatus = lunas |
Cetak SPK (PDF)
Kasir & Pembayaran
Pembayaran penuh, DP, pelunasan, invoice, efek ke keuangan dan CRM.
Pembayaran Penuh (Lunas)
- 1Kasir pilih SPK dari antrian bayar
- 2Jumlah = total tagihan, metode: Tunai → Submit
paymentStatus= lunas- Payment record type =
full. Invoice record dibuat - Journal entry: Dr. Kas / Cr. Pendapatan Jasa + Part + PPN
- WS wo:paid. Email Invoice ke customer
DP → Pelunasan (2 Transaksi)
- 1Bayar 50% dari total → type =
dp - 2Bayar sisa 50% → type =
pelunasan
- Setelah DP:
paymentStatus= dp. Belum bisa diserahkan - Setelah pelunasan: akumulasi = total. Status = lunas
- Journal entry terpisah untuk masing-masing transaksi
Pembayaran Melebihi Total
- 1Masukkan jumlah melebihi total tagihan → submit
Cetak Invoice
Cash Drawer & Shift Kasir
Buka/tutup shift, saldo kas, selisih, blokir payment tanpa shift aktif.
Buka Shift
- 1Kasir → Shift → Buka Shift, isi saldo awal kas
open. WS cash:drawer_opened. Hanya 1 shift aktif per branch.Tutup Shift → Hitung Selisih
- 1Klik Tutup Shift, isi saldo akhir (hitung fisik)
Payment Tanpa Shift Aktif
- 1Kasir proses pembayaran saat tidak ada shift aktif
Saldo Kas Real-Time saat Shift
Mekanik — Antrian & Pengerjaan Job
Isolasi antrian per mekanik, timer pengerjaan, pause/resume, trigger WS.
Antrian Terisolasi per Mekanik
waiting, running, paused, done terlihat.Mulai Job → startedAt & WS
- 1Klik "Mulai" pada job
waiting
running. startedAt terisi. WS wo:job_started. SA terima notifikasi.Pause & Resume — Akumulasi Waktu
- 1Job
running→ Pause - 2Resume
Selesaikan Job → Trigger wo:ready_to_pay
- 1Job terakhir di SPK → Klik Selesai
done. actualMinutes dihitung. WS wo:job_done. Jika semua job done → WS wo:ready_to_pay ke kasir.Parts Request (Mekanik → Gudang)
Create, approve/reject, efek ke stok, email notification, low stock trigger.
Buat Parts Request → Email ke Gudang/Manager
pending. WS parts:requested → gudang. Email PR Approval ke semua gudang/manager di branch.Approve → Stok Berkurang + Low Stock Check
approved. Stok −qty. Movement tipe keluar. WS parts:approved → mekanik. Jika stok ≤ minStock → WS stock:low + email alert.Reject → Stok Tidak Berubah
rejected. Stok tidak berubah. WS parts:rejected + alasan ke mekanik.Approve dengan Stok = 0
pending.Inventaris — Stok, Mutasi, Transfer
Masuk/keluar/adjustment, transfer antar cabang, alert minimum stok.
Penerimaan Stok (Masuk)
masuk. stockAfter terisi nilai baru.Adjustment — Set Nilai Absolut
stockAfter = 12.Keluar Melebihi Stok Tersedia
Transfer Stok Antar Cabang (3 Fase)
| Fase | Status | Efek Stok |
|---|---|---|
| Buat transfer Branch A→B qty=5 | pending | Tidak berubah |
| Admin approve | approved | Branch A −5, movement transfer_out |
| Branch B konfirmasi terima | received | Branch B +5, movement transfer_in |
Purchase Order (PO)
Buat, approve, receive, return ke supplier. Efek ke stok dan finance AP.
Alur PO: Draft → Approved → Received
| Step | Status | Efek |
|---|---|---|
| Buat PO + item | draft | displayId tergenerate |
| Submit | submitted | Approval flow aktif |
| Manager approve | approved | - |
| Receive barang | received | Stok +qty, Movement masuk, AP bertambah |
Return ke Supplier
CRM — Pelanggan, Kendaraan, Followup
Registrasi, loyalty tier, followup otomatis, STNK expired, service reminder email.
Registrasi Pelanggan + Kendaraan
loyaltyTier = bronze, totalSpend = 0. Vehicle terhubung ke customer. Kendaraan muncul di dropdown check-in.totalSpend Update setelah SPK Lunas
lastVisit diperbarui, totalSpend bertambah, riwayat servis muncul di profil.Loyalty Tier Naik (Cron)
| totalSpend | Tier |
|---|---|
| < 1.000.000 | Bronze |
| 1 jt – 4,9 jt | Silver |
| 5 jt – 14,9 jt | Gold |
| ≥ 15 jt | Platinum |
Followup Otomatis (Inactive + STNK)
- Pelanggan tidak berkunjung > 6 bulan → followup tipe
inactive_customer - Kendaraan STNK expired → followup tipe
stnk_expired
Service Reminder Email (Cron 09:00)
HCM — Karyawan, Absensi, KPI, Payroll
Tambah karyawan, absensi harian, input KPI, proses payroll dengan jurnal otomatis.
Tambah Karyawan → Muncul di Dropdown Mekanik
Absensi Clock In / Clock Out
hadir. Clock Out: jam keluar tersimpan, durasi dihitung, keterlambatan dicatat.Payroll → Jurnal Keuangan Otomatis
Keuangan (Finance)
COA, jurnal manual/otomatis, neraca balanced, cashflow, budget vs aktual, fiscal period lock.
Jurnal Manual — Harus Balanced
Jurnal Otomatis dari Payment SPK
| Akun | Debit | Kredit |
|---|---|---|
| Kas / 1100 | 222.000 | - |
| Pendapatan Jasa / 4100 | - | porsi jasa |
| Pendapatan Parts / 4200 | - | porsi parts |
| PPN Keluaran / 2300 | - | 22.000 |
Neraca — Total Aset = Kewajiban + Ekuitas
Fiscal Period Lock
- 1Admin tutup periode bulan lalu
- 2Coba buat jurnal dengan tanggal bulan tsb
Notifikasi Real-Time (WebSocket + Email)
17 WS event types, 8 email triggers, koneksi indikator, routing ke role yang tepat.
📡 WebSocket Events
| Event | Trigger | Target |
|---|---|---|
| wo:created | Check-in buat SPK | Branch |
| wo:status_changed | Status transition | Branch |
| wo:job_started | Mekanik mulai job | Branch |
| wo:job_done | Mekanik selesai job | Branch |
| wo:ready_to_pay | Semua job done | role:kasir |
| wo:paid | Payment lunas | Branch |
| parts:requested | Mekanik buat PR | role:gudang |
| parts:approved | Gudang approve PR | role:mekanik |
| parts:rejected | Gudang reject PR | role:mekanik |
| booking:created | Buat booking | Branch |
| bay:occupied | Check-in assign bay | Branch |
| bay:free | WO diserahkan | Branch |
| stock:low | Stok ≤ minStock | role:gudang |
| stock:transfer | Transfer stok | Branch |
| cash:drawer_opened | Buka shift | Branch |
| cash:drawer_closed | Tutup shift | Branch |
📧 Email Triggers
| Trigger | Penerima | |
|---|---|---|
| Welcome | Admin buat user baru | User baru |
| Reset Password | Forgot password | User |
| Booking Conf. | Booking dibuat | Customer |
| Invoice | Payment lunas | Customer |
| Vehicle Ready | WO → selesai | Customer |
| Service Reminder | Cron 09:00, 3 bln | Customer |
| Low Stock Alert | Stok ≤ minStock | Manager/Admin |
| PR Approval | Mekanik buat PR | Gudang/Manager |
WS Connection Indicator
Routing WS ke Role yang Tepat
- wo:ready_to_pay hanya kasir yang terima
- parts:requested hanya gudang yang terima
- parts:approved/rejected hanya mekanik yang terima
- stock:low hanya gudang yang terima
Email Booking Confirmation (< 30 detik)
Email Invoice + Vehicle Ready setelah Lunas/Selesai
- Payment lunas → invoice otomatis ke email customer
- WO → selesai → "Kendaraan Siap" ke email customer
- Jika customer tidak punya email → tidak error, silent skip
RBAC & Kontrol Akses Per Role
Isolasi role, branch scope, custom role, page-level ACL.
🗂️ Matriks Akses Fitur per Role
| Fitur | admin | manager | sa | mekanik | kasir | gudang | operator |
|---|---|---|---|---|---|---|---|
| Buat SPK | ✓ | ✓ | ✓ | − | − | − | ✓ |
| Proses Pembayaran | ✓ | ✓ | − | − | ✓ | − | − |
| Approve PO | ✓ | ✓ | − | − | − | − | − |
| Approve Parts Request | ✓ | ✓ | − | − | − | ✓ | − |
| Buat User | ✓ | − | − | − | − | − | − |
| Lihat Finance / Jurnal | ✓ | ✓ | − | − | − | − | − |
| Mutasi Stok Manual | ✓ | ✓ | − | − | − | ✓ | − |
| Dashboard Konsolidasi | ✓ | ✓ | − | − | − | − | − |
Kasir Tidak Bisa Akses Finance
- 1Login kasir → akses URL
/finance/jurnallangsung
Mekanik Hanya Lihat Job Sendiri
Manager Terbatas pada Branch Sendiri
branchScope middleware memfilter otomatis di API.Custom Role + Page-Level ACL
can('finance', 'view') = false jika tidak diberi permission.deniedPages=['/finance'] pada manager B → akses /finance ditolak meski role normalnya bisa.Matriks Efek Lintas Modul
Setiap aksi kunci dan modul mana saja yang ikut terpengaruh secara otomatis.
| Aksi | WO | Stok | Finance | CRM | Bay | WS Event | Audit | |
|---|---|---|---|---|---|---|---|---|
| Check-in (buat SPK) | Create | − | − | lastVisit | Occupied | wo:created | − | ✓ |
| Tambah item Part ke SPK | Recalc | − | − | − | − | − | − | ✓ |
| Assign Mekanik | ✓ | − | − | − | − | − | − | ✓ |
| WO → pengerjaan | ✓ | − | − | − | − | wo:status_changed | − | ✓ |
| Job mulai | startedAt | − | − | − | − | wo:job_started | − | − |
| Job selesai | done | − | − | − | − | wo:job_done | − | − |
| Semua job selesai | ✓ | − | − | − | − | wo:ready_to_pay → kasir | − | − |
| WO → selesai | ✓ | − | − | − | − | wo:status_changed | Vehicle Ready | ✓ |
| Payment Lunas | payStatus | − | Journal | totalSpend | − | wo:paid | Invoice | ✓ |
| WO → diserahkan | ✓ | − | − | lastVisit | Free | bay:free | − | ✓ |
| Parts Request create | − | − | − | − | − | parts:requested → gudang | PR Approval | − |
| Parts Request approve | − | Keluar | − | − | − | parts:approved → mekanik | − | − |
| PO Receive | − | Masuk | AP | − | − | − | − | ✓ |
| Stok ≤ minStock | − | − | − | − | − | stock:low → gudang | Low Stock | − |
| Buat Booking | − | − | − | − | − | booking:created | Booking Conf. | − |
| Buka Shift Kasir | − | − | − | − | − | cash:drawer_opened | − | − |
| Payroll Approved | − | − | Journal | − | − | − | − | ✓ |
| Buat User | − | − | − | − | − | − | Welcome | ✓ |
| Forgot Password | − | − | − | − | − | − | Reset Link | − |
Checklist Regresi Sebelum Release
Minimum skenario yang harus diverifikasi sebelum setiap deployment ke production.
🔴 Wajib Lulus — Core Flow
- Login semua 7 role berhasil dan redirect ke halaman yang benar
- Golden Path end-to-end (TC-FLOW-01) berjalan tanpa error
- Payment lunas → journal keuangan terbuat otomatis
- Payment lunas → email invoice terkirim (cek inbox atau log)
- Parts request approve → stok berkurang
- Stok ≤ minStock → WS event + email alert berjalan
🟠 Wajib Lulus — Security & Integrity
- WO selesai → email "kendaraan siap" terkirim ke customer
- Manager hanya lihat data branch sendiri
- Mekanik hanya lihat job milik sendiri
- Cash drawer: tidak bisa payment tanpa shift aktif
- Fiscal period closed → tidak bisa buat jurnal di periode tsb
- Dashboard angka konsisten dengan laporan detail
👤 Pelaku Eksternal (Sample)
| Nama | Kendaraan | Skenario |
|---|---|---|
| Pak Ahmad Fauzi | Honda Jazz B-9988-KZ (mesin mati mendadak) | SC-01 |
| Pak Budi Santoso | Toyota Avanza B-1234-AB (servis berkala) | SC-02 |
| Ibu Dewi Rahayu | Suzuki Ertiga D-4421-RH (ganti kampas rem) | SC-03 |
| Pak Rudi Hermawan | Toyota Kijang Innova B-7712-MN (overhaul mesin) | SC-04 |
| Ibu Sinta Wulandari | Honda Beat D-5678-XY (ganti oli) | SC-05 |
🏢 Pelaku Internal — Akun Seed
| Nama | Role | Email Login | Portal |
|---|---|---|---|
| Dewi Operator | operator | operator@bengkelpintar.com | /operator |
| Andi SA | sa | sa@bengkelpintar.com | /sa |
| Siti Kasir | kasir | kasir@bengkelpintar.com | /kasir |
| Joko Mekanik | mekanik | mekanik@bengkelpintar.com | /mekanik |
| Agus Prasetyo | mekanik | employee (no login) | — |
| Budi Prayitno | karu | karu@bengkelpintar.com | /mekanik |
| Rudi Gudang | gudang | gudang@bengkelpintar.com | /dashboard |
| Budi Manager | manager | manager@bengkelpintar.com | /dashboard |
Semua akun seed: password password123
🔧 Tanggung Jawab per Role
| Role | Tanggung Jawab Utama |
|---|---|
| operator | Booking, check-in kendaraan, follow-up pelanggan |
| sa | Buat & kelola SPK, assign mekanik, quality check, serahkan kendaraan |
| kasir | Proses pembayaran, kelola shift & laci kas, cetak invoice |
| mekanik | Kerjakan job, request parts, track waktu aktual |
| karu | Kepala Regu — supervisi, quality check, approve parts request |
| gudang | Kelola stok, approve & keluarkan parts, terima barang PO |
| manager | Approve Purchase Order, pantau laporan & KPI seluruh cabang |
📋 Ringkasan 8 Skenario
| ID | Nama Skenario | Trigger | Pelaku Utama | Highlight |
|---|---|---|---|---|
| SC-01 | Booking Darurat → SPK | Ahmad Hidayat — Honda Jazz mogok | Dewi · Andi · Joko · Siti | Walk-in langsung check-in, tanpa booking terjadwal |
| SC-02 | SPK Servis Berkala Penuh | Budi Santoso — Avanza 35.000 km | Dewi · Andi SA · Joko · Siti | Happy path lengkap dari booking sampai diserahkan |
| SC-03 | SPK + Parts Request & PO | Citra Dewi — Ertiga, kampas rem habis | Andi SA · Joko · Rudi Gudang · Budi Manager · Siti | Parts habis → PR → PO → terima barang → lanjut servis |
| SC-04 | SPK DP + Pelunasan | Dian Purnama — Innova overhaul | Andi SA · Joko · Siti | DP dulu → pengerjaan → pelunasan → diserahkan |
| SC-05 | Booking No Show → Follow-up | Eko Prasetyo — tidak hadir | Dewi · Sistem | No show → follow-up manual → reschedule |
| SC-06 | Kasir — Buka Shift, Bayar, Tutup Shift | Hari kerja Siti Kasir | Siti Kasir | Shift open → 3 pembayaran (cash/transfer/DP) → shift report → tutup |
| SC-07 | Gudang — Terima PO, Kelola Stok, Fulfill PR | Joko request parts untuk WO | Joko Mekanik · Rudi Gudang · Budi Manager | Parts request → stok kurang → PR → PO → terima → serahkan ke mekanik |
| SC-08 | Finance — Jurnal Manual, Tutup Periode, Laporan | Budi Manager buat penyesuaian | Budi Manager | Jurnal draft → post → cek ledger → tutup periode → cetak P&L |
SC-01: Booking Darurat → SPK
Pelanggan datang mendadak (walk-in) karena kendaraan bermasalah — tidak ada booking sebelumnya. Operator langsung proses check-in dan SA segera buat SPK dengan prioritas antrian lebih cepat.
👥 Pelaku
⚡ Perbedaan dari Alur Normal
- Tidak ada booking terjadwal — langsung ke check-in
- Operator cari data pelanggan via nomor telepon/plat kendaraan
- Jika pelanggan baru: buat akun customer baru dulu di sistem
- SPK dibuat langsung dengan status antrian atau bisa langsung diagnosa jika bay tersedia
- SA mengisi keluhan berdasarkan keterangan pelanggan lisan
📍 Entry Point di Admin
- AOperator buka /operator/checkin → cari pelanggan/kendaraan
- BSA buka /sa/create → buat SPK manual langsung
- !Kedua jalur hasilkan WO yang sama — pilih sesuai siapa yang melayani
🔄 Flow Diagram — SC-01
📋 SC-01: Step-by-Step Detail
| # | Pelaku | Aksi | Hasil / Efek Sistem |
|---|---|---|---|
| 1 | Pak Ahmad | Datang ke bengkel, menjelaskan ke Dewi: mesin Jazz tiba-tiba mati di jalan Sudirman, tidak mau nyala | — |
| 2 | Dewi (operator) | Buka /operator/checkin, cari "Ahmad Fauzi" → ditemukan, pilih Honda Jazz B-9988-KZ | Data pelanggan + kendaraan muncul |
| 2a | Dewi (operator) | (Jika pelanggan baru) Buat customer baru → daftarkan kendaraan | Customer ID + Vehicle ID baru tersimpan di CRM |
| 3 | Dewi (operator) | Input odometer: 48.200 km, keluhan: "Mesin mati mendadak, tidak mau start", Bay 2, submit check-in | WO dibuat: SPK-202604-0051, status antrian. vehicle.km=48200, lastVisit diperbarui. WS wo:created |
| 4 | Andi (sa) | Buka SPK-202604-0051, konfirmasi Bay 2 sudah di-assign | Bay 2 status → occupied. WS bay:occupied |
| 5 | Andi (sa) | Tambah item: jasa "Diagnosa Mesin" Rp 50.000. (item parts menyusul setelah diagnosa) | subtotal 50.000, PPn 5.500, total 55.500 |
| 6 | Andi (sa) | Assign Joko Mekanik ke SPK | MechanicAssignment dibuat, job "Diagnosa Mesin" ter-link ke Joko |
| 7 | Andi (sa) | Ubah status → diagnosa | Joko mendapat notifikasi job baru. WS wo:status_changed |
| 8 | Joko (mekanik) | Periksa Jazz: temukan aki lemah + busi mati. Lapor ke Andi. Andi tambah item: Busi x4 Rp 200.000 + Aki Rp 450.000 | Total diperbarui: Rp 776.250 (termasuk PPn) |
| 9 | Andi (sa) | Ubah status → pengerjaan | Validasi mekanik ✓. WS broadcast ke Joko |
| 10 | Joko (mekanik) | Klik Mulai job "Ganti Busi" → kerjakan → klik Selesai. Lanjut "Ganti Aki" | Semua job done. actualMinutes dicatat. KPI Joko diperbarui |
| 11 | Andi (sa) | Ubah status → quality_check | Validasi semua job done ✓ |
| 12 | Andi (sa) | Test starter Jazz — nyala normal. Ubah status → selesai | WS wo:ready_to_pay ke Siti (kasir). WA ke Pak Ahmad: "Honda Jazz Anda sudah siap" |
| 13 | Siti (kasir) | Pak Ahmad bayar tunai Rp 800.000. Siti input: method=cash, cashReceived=800000 | Kembalian Rp 23.750. paymentStatus → lunas. CashDrawer +776.250. Jurnal GL otomatis. WS wo:paid |
| 14 | Andi (sa) | Ubah status → diserahkan, serahkan kunci ke Pak Ahmad | Bay 2 → available. WS bay:free. totalSpend Pak Ahmad +776.250. Email invoice terkirim |
SC-02: SPK Servis Berkala — Happy Path Penuh
Skenario standar paling umum: pelanggan sudah booking sebelumnya, datang tepat waktu, servis berjalan lancar, bayar lunas, kendaraan diserahkan. Melibatkan seluruh modul operasional.
👥 Pelaku
📅 6 Status SPK Berurutan
| Status | Aktor | Syarat |
|---|---|---|
| antrian | Sistem (saat check-in) | — |
| diagnosa | SA | Ada mekanik di-assign |
| pengerjaan | SA | Semua job punya mekanik |
| quality_check | SA / Karu | Semua job status done |
| selesai | SA | QC lulus |
| diserahkan | SA / Kasir | paymentStatus = lunas |
⚙️ Efek Sistem Otomatis saat Diserahkan
- Bay status →
available, WSbay:free - Jurnal GL: Dr Kas / Cr Pendapatan Jasa + Dr HPP / Cr Persediaan Parts
- WhatsApp notifikasi terima kasih + rating request
- Follow-up
post_servicedijadwalkan otomatis - KPI mekanik diperbarui (job selesai + waktu aktual)
- CRM:
totalSpendbertambah,lastVisitdiperbarui
🔄 Flow Diagram — SC-02 (Booking → Diserahkan)
📋 SC-02: Step-by-Step Detail (20 Langkah)
| # | Pelaku | Aksi | Hasil / Efek Sistem |
|---|---|---|---|
| 1 | Dewi (operator) | Sistem kirim reminder servis berkala ke Pak Budi via WA (auto follow-up servis_berkala — 35.000 km tercapai) | Pak Budi balas mau booking |
| 2 | Dewi (operator) | Buat booking: Pak Budi Santoso, Avanza B-1234-AB, besok jam 09:00, type=reguler, layanan Tune Up + Ganti Oli | Booking BK-A10023 status scheduled. Email konfirmasi ke Pak Budi |
| 3 | Dewi (operator) | Telepon Pak Budi konfirmasi jadwal → PATCH /bookings/BK-A10023/confirm dengan mechanicIds: [Joko] | Status confirmed. Joko ter-assign ke booking |
| 4 | Pak Budi | Tiba tepat waktu jam 09:00 membawa Avanza | — |
| 5 | Dewi (operator) | PATCH status: arrived → check-in: odometer 35.420 km, Bay 3, keluhan "Servis rutin 35.000 km" | WO: SPK-202604-0042, status antrian. Bay 3 → occupied. PATCH /bookings/BK-A10023/link-wo menghubungkan booking ke SPK |
| 6 | Andi (sa) | Buka SPK-202604-0042, tambah jasa: Tune Up Rp 200.000 + Ganti Oli Rp 75.000 | subtotal 275.000, PPn 30.250, total 305.250 |
| 7 | Andi (sa) | Tambah parts: Filter Oli 1x Rp 45.000 + Oli Mesin 4L x Rp 85.000 | subtotal 725.000, PPn 79.750, total 804.750 |
| 8 | Andi (sa) | Assign Joko Mekanik ke SPK (sudah di-pre-assign dari booking confirm) | MechanicAssignment terkonfirmasi |
| 9 | Andi (sa) | Ubah status → diagnosa | Joko mendapat notifikasi job. WS wo:status_changed |
| 10 | Joko (mekanik) | Periksa kondisi Avanza — semua sesuai, tidak ada temuan tambahan. Lapor ke Andi | — |
| 11 | Andi (sa) | Ubah status → pengerjaan | Validasi mekanik di-assign ✓. WS broadcast ke Joko |
| 12 | Joko (mekanik) | Klik Mulai job "Tune Up" di portal mekanik /mekanik | Job running, startedAt 09:23. WS wo:job_started |
| 13 | Joko (mekanik) | Selesaikan Tune Up → Selesai. Mulai + selesaikan "Ganti Oli + Filter" | Semua job done. Joko: actualMinutes Tune Up=38m, Ganti Oli=22m. KPI diperbarui |
| 14 | Andi (sa) | Ubah status → quality_check | Validasi semua job done ✓ |
| 15 | Andi (sa) | Cek oli level, bersih — OK. Ubah status → selesai | WS wo:ready_to_pay → Siti (kasir). WA ke Pak Budi: "Avanza B-1234-AB Anda sudah siap" |
| 16 | Siti (kasir) | Terima notifikasi di portal kasir /kasir, buka antrian bayar SPK-202604-0042 | Total tagihan Rp 804.750 muncul |
| 17 | Siti (kasir) | Pak Budi bayar tunai Rp 810.000. Input: method=cash, cashReceived=810000 | Kembalian Rp 5.250. paymentStatus → lunas. CashDrawer Siti +804.750. Jurnal GL: Dr Kas / Cr Pendapatan |
| 18 | Siti (kasir) | Cetak invoice A4 + struk termal untuk Pak Budi | PDF invoice INV-SPK-202604-0042. Email invoice ke Pak Budi |
| 19 | Andi (sa) | Ubah status → diserahkan, serahkan kunci + struk ke Pak Budi | Bay 3 → available. WS bay:free. totalSpend Pak Budi +804.750. lastVisit diperbarui |
| 20 | Sistem | Auto: WA terima kasih ke Pak Budi + jadwalkan follow-up post_service (survey 3 hari kemudian oleh Dewi) | Follow-up entry dibuat, status pending, assigned ke Dewi |
SC-03: SPK dengan Parts Request & Purchase Order
Mekanik menemukan parts yang dibutuhkan tidak tersedia di gudang saat pengerjaan. Flow bercabang ke purchase request → approval manager → beli supplier → terima barang → servis dilanjutkan.
👥 Pelaku
📋 Status Parts Request
| Status | Artinya |
|---|---|
| pending | Mekanik submit request, menunggu review gudang |
| approved | Gudang setujui — stok dikeluarkan ke job |
| rejected | Gudang tolak (stok habis / salah part) → eskalasi ke PO |
| delivered | Parts sudah diserahkan ke mekanik, job bisa dilanjutkan |
🛒 Alur Purchase Order (jika stok habis)
- 1Admin gudang buat Purchase Request (PR)
- 2Manager approve PR → jadi Purchase Order (PO)
- 3Kirim PO ke supplier, tunggu pengiriman
- 4Terima barang: stok bertambah, biaya dicatat
- !SPK ditahan (on hold) selama menunggu parts tiba
🔄 Flow Diagram — SC-03 (SPK + Parts Request)
📋 SC-03: Step-by-Step Detail
| # | Pelaku | Aksi | Hasil / Efek Sistem |
|---|---|---|---|
| 1 | Andi (sa) | SPK Ertiga D-4421-RH sudah masuk status pengerjaan. Agus sedang kerjakan servis rutin | — |
| 2 | Agus (mekanik) | Saat cek rem depan, temukan kampas rem Ertiga sudah tipis 2mm — perlu diganti segera. Lapor ke Andi | Andi konfirmasi ke Ibu Dewi via WA, Ibu Dewi setuju |
| 3 | Andi (sa) | Tambah item parts "Kampas Rem Depan Ertiga x2 @ Rp 180.000" ke SPK | Total tagihan bertambah +399.600 (incl. PPn) |
| 4 | Agus (mekanik) | Submit parts request di portal: partId=KAMPAS-ERTIGA-D, qty=2, alasan="Kampas rem tipis 2mm, perlu ganti" | Parts request PR-0089 status pending. WS parts:requested → Rudi (gudang). Email ke Rudi + Budi |
| 5 | Rudi (gudang) | Buka antrian parts request, cek stok "Kampas Rem Ertiga" di sistem | BranchStock = 0 — tidak bisa approve |
| 6 | Rudi (gudang) | PATCH /api/mekanik/parts-requests/PR-0089/reject → buat Purchase Request ke supplier "Mitra Autosupply", qty=10 | PR-0089 status rejected. Purchase Request PR-PO-0031 dibuat via POST /api/purchase-requests |
| 7 | Budi (manager) | Terima notifikasi approval PR-PO-0031, review spesifikasi + harga, PATCH /api/purchase-requests/PR-PO-0031/status → approved | Purchase Request disetujui. Rudi dapat notifikasi untuk buat PO |
| 8 | Rudi (gudang) | Buat PO: POST /api/purchase-orders untuk Mitra Autosupply, kirim ke supplier, update status: PATCH /api/purchase-orders/:id/status → dikirim | PO-0031 status → dikirim |
| 9 | Rudi (gudang) | Barang datang keesokan harinya — goods receipt: POST /api/purchase-orders/PO-0031/receive, verifikasi 10 pcs kampas rem sesuai PO | BranchStock "Kampas Rem Ertiga" +10. StockMovement: masuk. PO status → diterima |
| 10 | Rudi (gudang) | PATCH /api/mekanik/parts-requests/PR-0089/deliver — keluarkan 2 kampas rem ke Agus | PR-0089 status delivered. BranchStock -2. StockMovement: keluar. Agus dapat notifikasi "Parts siap" |
| 11 | Agus (mekanik) | Ambil kampas rem dari Rudi, lanjutkan pengerjaan — pasang kampas rem depan kiri & kanan | Job "Ganti Kampas Rem" → running → done, actualMinutes=45m |
| 12 | Andi (sa) | Semua job Agus done → QC (cek pengereman) → ubah status → selesai | WS wo:ready_to_pay → Siti. WA ke Ibu Dewi: "Ertiga Anda sudah siap" |
| 13 | Siti (kasir) | Proses pembayaran Ibu Dewi: total termasuk kampas rem + jasa. Method=transfer, reference="TRF-BCA-7712" | paymentStatus → lunas. Jurnal GL: Dr Kas / Cr Pendapatan + Dr HPP Rp 270.000 / Cr Persediaan. Email invoice ke Ibu Dewi |
SC-04: Pembayaran Bertahap — DP + Pelunasan
Pelanggan tidak bisa bayar lunas di awal. Kasir proses Down Payment (uang muka) untuk memulai pengerjaan, lalu menerima pelunasan sisa tagihan saat kendaraan selesai dan siap diserahkan.
👥 Pelaku
💳 3 Tipe Pembayaran
| type | Kapan dipakai | paymentStatus WO |
|---|---|---|
dp | Uang muka pertama, paidAmount < total | dp |
lunas | Bayar langsung penuh sekaligus | lunas |
pelunasan | Bayar sisa setelah sebelumnya ada DP | lunas |
⚠️ Aturan Penting
- Status diserahkan hanya bisa jika paymentStatus =
lunas - Kasir harus punya shift aktif (CashDrawer open) untuk proses payment
- Setiap pembayaran langsung dicatat ke laci kas dan jurnal GL
- DP bisa dilakukan kapan saja, termasuk sebelum pengerjaan dimulai
- Kembalian (change) hanya berlaku untuk metode cash
🔄 Flow Diagram — SC-04 (DP + Pelunasan)
📋 SC-04: Step-by-Step Detail
| # | Pelaku | Aksi | Hasil / Efek Sistem |
|---|---|---|---|
| 1 | Andi (sa) | SPK dibuat untuk overhaul mesin Innova B-7712-MN — estimasi total Rp 4.200.000, dikerjakan Joko & Agus | paymentStatus belum |
| 2 | Siti (kasir) | Siti buka shift pagi: POST /api/kasir/shift/open, saldo awal Rp 500.000 | CashDrawer status open |
| 3 | Siti (kasir) | Pak Rudi bayar DP tunai Rp 2.000.000. Input: type=dp, method=cash, amount=2000000, cashReceived=2000000 | paymentStatus → dp. paidAmount=2.000.000. CashDrawer Siti +2.000.000. Jurnal: Dr Kas 2.000.000 / Cr Uang Muka Pelanggan 2.000.000 |
| 4 | Andi (sa) | Konfirmasi DP diterima, pindahkan status: antrian → diagnosa → pengerjaan. Assign Joko & Agus | Joko dan Agus mendapat notifikasi job overhaul |
| 5 | Joko + Agus | Kerjakan overhaul mesin bersama — proses 2-3 hari. Joko: bongkar mesin & ganti bearing. Agus: gasket & assembly | Setiap job dicatat startedAt / pausedAt / completedAt. KPI masing-masing tercatat |
| 6 | Budi Prayitno (karu) | Budi (karu) lakukan quality check mesin: cek tekanan kompresi, kebocoran oli, uji coba jalan | — |
| 6a | Andi (sa) | QC lulus → ubah status → selesai | WA ke Pak Rudi: "Innova Anda siap, sisa tagihan Rp 2.200.000" |
| 7 | Pak Rudi | Pak Rudi datang ke bengkel untuk ambil kendaraan | — |
| 8 | Siti (kasir) | Cek SPK: total 4.200.000, DP sudah 2.000.000, sisa 2.200.000. Input pelunasan: type=pelunasan, method=transfer, amount=2200000, reference="TRF-BRI-08441" | paymentStatus → lunas. paidAmount=4.200.000. Jurnal: Dr Transfer Masuk 2.200.000 / Cr Pendapatan 4.200.000 + Dr Uang Muka 2.000.000 |
| 9 | Siti (kasir) | Cetak invoice A4 — menampilkan 2 baris pembayaran: DP Rp 2.000.000 (tunai) + Pelunasan Rp 2.200.000 (transfer) | Email invoice dikirim ke Pak Rudi |
| 10 | Andi (sa) | Ubah status → diserahkan, serahkan kunci + buku servis ke Pak Rudi | Validasi lunas ✓. Bay → available. CRM update +4.200.000. WA terima kasih + rating |
SC-05: Booking No Show → Follow-up → Reschedule
Pelanggan sudah booking dan dikonfirmasi, tapi tidak hadir di hari yang dijadwalkan. Sistem dan operator menangani dengan follow-up CRM dan menjadwalkan ulang.
👥 Pelaku
📅 Status Booking Terlibat
| Status | Kapan |
|---|---|
| confirmed | Sudah dikonfirmasi ke pelanggan |
| no_show | Hari H pelanggan tidak datang — operator update manual |
📬 Status Follow-up
| Status | Kapan |
|---|---|
pending | Follow-up masuk antrian |
contacted | Operator sudah hubungi |
scheduled | Pelanggan mau reschedule |
skipped | Tidak bisa dihubungi / tidak relevan |
🤖 Follow-up Otomatis dari Sistem
- Setelah booking no_show: sistem buat follow-up tipe
manual - Servis berkala terlewat: trigger
servis_berkalaotomatis - STNK mau habis: trigger
stnk_expiredotomatis - Tidak servis > 3 bulan: trigger
inactive_customer - Setelah servis selesai:
post_servicesurvey kepuasan
🔄 Flow Diagram — SC-05 (No Show → Follow-up)
📋 SC-05: Step-by-Step Detail
| # | Pelaku | Aksi | Hasil / Efek Sistem |
|---|---|---|---|
| 1 | Dewi (operator) | Booking Ibu Sinta Wulandari (Honda Beat D-5678-XY) Senin 09:00, type=reguler, layanan Ganti Oli. PATCH /confirm → status confirmed | Email + WA konfirmasi terkirim ke Ibu Sinta |
| 2 | Dewi (operator) | Senin 09:30 — Ibu Sinta belum muncul. Dewi update: PATCH /bookings/:id/status → no_show | Booking no_show. Bay tidak jadi dipakai. Dewi catat catatan di sistem |
| 3 | Dewi (operator) | Buat follow-up manual: POST /api/operator/followup, tipe=manual, alasan="Tidak hadir booking Senin 09:00 — Honda Beat D-5678-XY" | Follow-up FU-0312 status pending. Masuk antrian CRM Dewi |
| 4 | Dewi (operator) | Senin pukul 12:00 — Dewi hubungi Ibu Sinta via WA | — |
| 5 | Ibu Sinta | Balas WA Dewi: "Maaf Mbak Dewi, tadi ada keperluan mendadak. Bisa Rabu jam 10 ya?" | — |
| 5a | Dewi (operator) | PATCH follow-up FU-0312: status=contacted, method="whatsapp", notes="Pelanggan minta reschedule Rabu 10:00" | Follow-up status contacted |
| 6 | Dewi (operator) | PATCH follow-up FU-0312: status=scheduled, nextFollowup=Rabu | Follow-up scheduled. Reminder diset untuk Rabu pagi |
| 7 | Dewi (operator) | Buat booking baru: Ibu Sinta, Beat D-5678-XY, Rabu 10:00, Ganti Oli | Booking BK-A10041 status scheduled |
| 8 | Dewi (operator) | WA konfirmasi ulang ke Ibu Sinta → PATCH /bookings/BK-A10041/confirm | Status confirmed. Email konfirmasi terkirim |
| 9 | Ibu Sinta | Rabu pagi pukul 09:55 — Ibu Sinta hadir membawa Honda Beat | — |
| 10 | Dewi (operator) | PATCH booking: arrived → check-in kendaraan, odometer 18.750 km, Bay 1 | SPK baru SPK-202604-0055 dibuat. Booking BK-A10041 linked ke SPK. Lanjut ke alur SC-02 (Andi, Joko, Siti) |
| 11 | Sistem | 3 hari setelah servis selesai: sistem auto-buat follow-up post_service untuk Ibu Sinta | Follow-up baru status pending masuk antrian Dewi — minta rating kepuasan & reminder servis berikutnya |
skipped dengan catatan. Sistem akan mencoba lagi melalui trigger inactive_customer jika pelanggan tidak servis lebih dari 3 bulan. Data riwayat no-show tercatat di CRM untuk analisis loyalitas pelanggan.
SC-06: Kasir — Buka Shift, Proses Pembayaran, Tutup Shift
Skenario hari kerja penuh seorang kasir: membuka laci kas di pagi hari, memproses beberapa pembayaran (tunai, transfer, dan DP), mencatat kas keluar (petty cash), lalu mencetak laporan shift dan menutup laci kas di akhir hari.
👥 Pelaku
🔑 Endpoint Kasir Utama
| Aksi | Endpoint | Role |
|---|---|---|
| Buka shift | POST /api/kasir/shift/open | kasir, admin, manager |
| Tutup shift | POST /api/kasir/shift/close | kasir, admin, manager |
| Cek shift aktif | GET /api/kasir/shift/current | kasir, admin, manager |
| Riwayat shift | GET /api/kasir/shift/history | kasir, admin, manager |
| Proses pembayaran | POST /api/workorders/:id/payments | kasir, admin, manager |
| List pembayaran WO | GET /api/workorders/:id/payments | kasir, sa, admin, manager |
| Antrian bayar | GET /api/kasir/queue | kasir, admin, manager |
| Kas masuk manual | POST /api/kasir/cash/in | kasir, admin, manager |
| Kas keluar manual | POST /api/kasir/cash/out | kasir, admin, manager |
| Transaksi shift aktif | GET /api/kasir/cash/transactions | kasir, admin, manager |
| Ringkasan shift | GET /api/kasir/cash/summary | kasir, admin, manager |
| Invoice PDF (A4) | GET /api/kasir/invoices/:woId/pdf | kasir, admin, manager |
| Struk termal | GET /api/kasir/invoices/:woId/receipt | kasir, admin, manager |
💳 Tipe Pembayaran & Metode
| type | Kapan | paymentStatus WO |
|---|---|---|
lunas | Bayar penuh sekaligus | lunas |
dp | Uang muka, paidAmount < total | dp |
pelunasan | Bayar sisa setelah DP | lunas |
| method | Keterangan |
|---|---|
cash | Tunai — bisa hitung kembalian |
transfer | Transfer bank — wajib isi reference |
qris | QRIS / e-wallet |
debit | Kartu debit / kredit |
🔄 Flow Diagram — SC-06 (Kasir Daily Flow)
📋 SC-06: Step-by-Step Detail
/kasir · Tiga transaksi: cash lunas, transfer lunas, DP| # | Pelaku | Aksi | Hasil / Efek Sistem |
|---|---|---|---|
| 1 | Siti (kasir) | Login ke http://localhost:4100/kasir dengan kasir@bengkelpintar.com / password123 | Portal kasir terbuka. Siti melihat status shift belum aktif |
| 2 | Siti (kasir) | Buka laci kas: POST /api/kasir/shift/open dengan body {"openingBalance": 500000} | CashDrawer open. WS cash:drawer_opened broadcast. Audit log dicatat. Siti bisa mulai proses transaksi |
| 3 | Siti (kasir) | Lihat antrian: GET /api/kasir/queue — tampil daftar WO siap bayar hari ini | Muncul 3 WO siap bayar: Ahmad Hidayat, Budi Santoso, Citra Dewi |
| 4 | Ahmad Hidayat | Datang ke kasir untuk bayar Tune Up + Ganti Oli, total Rp 804.750 | — |
| 5 | Siti (kasir) | Proses pembayaran tunai: POST /api/workorders/:woId/payments — {"type":"lunas","method":"cash","amount":804750,"cashReceived":810000} | paymentStatus WO → lunas. Kembalian Rp 5.250. CashDrawer Siti +804.750. WS wo:paid. Email invoice ke Ahmad |
| 6 | Siti (kasir) | Cetak invoice A4: GET /api/kasir/invoices/:woId/pdf dan struk termal: GET /api/kasir/invoices/:woId/receipt | PDF invoice INV-SPK-202604-XXXX dibuka di tab baru. Struk termal dicetak |
| 7 | Budi Santoso | Bayar servis berkala total Rp 1.250.000 via transfer BCA | — |
| 8 | Siti (kasir) | Proses pembayaran transfer: POST /api/workorders/:woId/payments — {"type":"lunas","method":"transfer","amount":1250000,"reference":"TRF-BCA-8812"} | paymentStatus WO → lunas. CashDrawer: kategori transfer +1.250.000. Email invoice ke Budi |
| 9 | Citra Dewi | Datang untuk bayar DP overhaul kopling. Total estimasi Rp 3.200.000, ingin DP Rp 1.500.000 | — |
| 10 | Siti (kasir) | Proses DP: POST /api/workorders/:woId/payments — {"type":"dp","method":"cash","amount":1500000,"cashReceived":1500000} | paymentStatus WO → dp. paidAmount=1.500.000. CashDrawer +1.500.000. WO belum bisa diserahkan sebelum lunas |
| 11 | Siti (kasir) | Catat pengeluaran petty cash: POST /api/kasir/cash/out — {"amount":45000,"note":"Pembelian ATK kantor"} | CashTransaction tipe keluar dicatat di shift. CashDrawer -45.000 |
| 12 | Siti (kasir) | Akhir hari — cek ringkasan shift: GET /api/kasir/cash/summary | Ringkasan: total masuk Rp 3.554.750, total keluar Rp 45.000, kas berjalan Rp 4.009.750 |
| 13 | Siti (kasir) | Hitung uang fisik di laci kas. Tutup shift: POST /api/kasir/shift/close — {"actualCash":2009750,"closingBalance":2009750,"notes":"Shift sore selesai"} | CashDrawer status → closed. Selisih dihitung. WS cash:drawer_closed. Laporan shift bisa diunduh dari GET /api/kasir/shift/history |
SC-07: Gudang — Terima PO, Kelola Stok, Fulfill Parts Request
Mekanik sedang mengerjakan SPK dan membutuhkan parts yang stoknya kurang. Skenario lengkap dari request mekanik, gudang cek stok, buat Purchase Request, manager approve, buat PO, supplier kirim barang, gudang terima dan serahkan ke mekanik.
👥 Pelaku
🔩 Endpoint Parts Request
| Aksi | Endpoint | Role |
|---|---|---|
| Submit request | POST /api/mekanik/parts-request | mekanik, karu |
| List requests | GET /api/mekanik/parts-requests | mekanik, gudang, manager |
| Approve (potong stok) | PATCH /api/mekanik/parts-requests/:id/approve | gudang, admin, manager |
| Reject | PATCH /api/mekanik/parts-requests/:id/reject | gudang, admin, manager |
| Delivered ke mekanik | PATCH /api/mekanik/parts-requests/:id/deliver | gudang, admin, manager |
🛒 Endpoint Purchase & Stok
| Aksi | Endpoint | Role |
|---|---|---|
| Cek stok cabang | GET /api/stock/branch-stock | semua |
| Stok low alert | GET /api/parts/low-stock | semua |
| Buat Purchase Request | POST /api/purchase-requests | gudang, manager |
| Approve PR | PATCH /api/purchase-requests/:id/status | admin, manager |
| Buat PO | POST /api/purchase-orders | gudang, manager |
| Update status PO | PATCH /api/purchase-orders/:id/status | gudang, manager |
| Terima barang (GR) | POST /api/purchase-orders/:id/receive | gudang, manager |
| Catat gerakan stok | POST /api/stock/movements | gudang, admin, manager |
🔄 Flow Diagram — SC-07 (Procurement Chain Lengkap)
📋 SC-07: Step-by-Step Detail
| # | Pelaku | Aksi | Hasil / Efek Sistem |
|---|---|---|---|
| 1 | Joko (mekanik) | Sedang kerjakan SPK-202604-0073 (servis Dian Purnama). Menemukan Filter Bahan Bakar perlu ganti — cek stok di portal mekanik | Stok tersedia: 0 pcs di gudang cabang |
| 2 | Joko (mekanik) | Submit parts request: POST /api/mekanik/parts-request — {"partId":"<uuid>","partName":"Filter Bahan Bakar","qty":3,"workOrderId":"<woId>","reason":"Filter bahan bakar kotor dan perlu ganti"} | PartsRequest PR-0089 status pending. WS parts:requested → Rudi. Email notifikasi ke Rudi Gudang & Budi Manager |
| 3 | Rudi (gudang) | Terima notifikasi, buka admin portal. Cek stok: GET /api/stock/branch-stock?search=Filter+Bahan | BranchStock = 0 — tidak cukup untuk approve |
| 4 | Rudi (gudang) | Reject parts request: PATCH /api/mekanik/parts-requests/PR-0089/reject — {"notes":"Stok habis, akan pengadaan baru"} | PR-0089 status → rejected. WS parts:rejected → Joko. Joko tahu harus pause job |
| 5 | Rudi (gudang) | Buat Purchase Request: POST /api/purchase-requests — {"items":[{"partId":"<uuid>","name":"Filter Bahan Bakar","qty":10,"reason":"Stok habis, mendesak untuk WO aktif"}],"notes":"Urgent — WO aktif menunggu"} | Purchase Request PRQ-0031 status pending. Notifikasi ke Budi Manager |
| 6 | Budi Manager | Review Purchase Request PRQ-0031 di halaman /inventory. Setuju dengan harga dan kebutuhan. PATCH /api/purchase-requests/PRQ-0031/status — {"status":"approved"} | PRQ-0031 status → approved. Rudi mendapat notifikasi untuk lanjut buat PO |
| 7 | Rudi (gudang) | Buat Purchase Order: POST /api/purchase-orders — {"supplierId":"<uuid>","items":[{"partId":"<uuid>","name":"Filter Bahan Bakar","qty":10,"unitPrice":85000}],"notes":"Urgent untuk WO aktif"} | PO-0031 status draft. Total PO Rp 850.000 |
| 8 | Rudi (gudang) | Kirim PO ke supplier Mitra Auto: PATCH /api/purchase-orders/PO-0031/status — {"status":"dikirim"} | PO-0031 status → dikirim. Supplier menerima PO via email |
| 9 | Rudi (gudang) | Barang tiba keesokan harinya — verifikasi 10 pcs Filter BB sesuai PO. Goods Receipt: POST /api/purchase-orders/PO-0031/receive | BranchStock Filter BB: 0 → 10. StockMovement masuk qty=10 dibuat. PO-0031 status → diterima |
| 10 | Rudi (gudang) | Buat parts request baru untuk Joko (approve langsung karena stok sudah ada): PATCH /api/mekanik/parts-requests/PR-0089-NEW/approve | Stok cukup — approve. BranchStock -3. StockMovement keluar qty=3. WS parts:approved → Joko |
| 11 | Rudi (gudang) | Serahkan 3 Filter BB secara fisik ke Joko: PATCH /api/mekanik/parts-requests/:id/deliver | PartsRequest status → delivered. WS parts:delivered → Joko. Joko dapat notifikasi "Parts siap diambil" |
| 12 | Joko (mekanik) | Ambil Filter BB dari Rudi, lanjutkan pengerjaan SPK-202604-0073 — pasang filter baru | Job "Ganti Filter BB" → running → done. Semua job selesai → WS wo:ready_to_pay → Kasir |
SC-08: Finance — Jurnal Manual, Tutup Periode, Laporan Keuangan
Manager keuangan membuat jurnal penyesuaian manual, memverifikasi buku besar, menutup periode fiskal bulan berjalan, lalu menghasilkan laporan Laba Rugi dan Neraca untuk dilaporkan ke manajemen.
👥 Pelaku
📒 Endpoint Finance — Jurnal
| Aksi | Endpoint | Role |
|---|---|---|
| List jurnal | GET /api/finance/journals | semua |
| Detail jurnal | GET /api/finance/journals/:id | semua |
| Buat jurnal draft | POST /api/finance/journals | admin, manager, kasir |
| Edit jurnal draft | PUT /api/finance/journals/:id | admin, manager, kasir |
| Post jurnal (draft→posted) | POST /api/finance/journals/:id/post | admin, manager |
| Void jurnal | POST /api/finance/journals/:id/void | admin, manager |
| Hapus jurnal draft | DELETE /api/finance/journals/:id | admin, manager |
| Buku besar | GET /api/finance/ledger | semua |
📅 Endpoint Periode & Laporan
| Aksi | Endpoint | Role |
|---|---|---|
| List periode | GET /api/finance/periods | semua |
| Tutup periode | POST /api/finance/periods/close | admin, manager |
| Buka kembali periode | PUT /api/finance/periods/:id/reopen | admin |
| Neraca saldo | GET /api/finance/reports/trial-balance | semua |
| Laba Rugi (P&L) | GET /api/finance/reports/pnl | semua |
| Neraca (Balance Sheet) | GET /api/finance/reports/balance-sheet | semua |
| Arus Kas | GET /api/finance/reports/cash-flow | semua |
| Export P&L Excel | GET /api/finance/reports/pnl/export | semua (token-based) |
| Export Neraca Excel | GET /api/finance/reports/trial-balance/export | semua (token-based) |
| Verifikasi GL seimbang | GET /api/finance/reports/gl/verify | semua |
🔄 Flow Diagram — SC-08 (Finance — Jurnal sampai Laporan)
📋 SC-08: Step-by-Step Detail
http://localhost:4100/finance · Periode: April 2026| # | Pelaku | Aksi | Hasil / Efek Sistem |
|---|---|---|---|
| 1 | Budi Manager | Login ke admin portal, buka modul Finance → Jurnal (/finance/jurnal) | Halaman daftar jurnal terbuka. Budi melihat jurnal-jurnal otomatis dari transaksi WO bulan ini |
| 2 | Budi Manager | Buat jurnal penyesuaian biaya penyusutan peralatan: POST /api/finance/journals — body: {"date":"2026-04-30","description":"Penyusutan peralatan bengkel April 2026","lines":[{"accountId":"<beban-penyusutan-coa-id>","debit":2500000,"credit":0},{"accountId":"<akumulasi-penyusutan-coa-id>","debit":0,"credit":2500000}]} | Jurnal JRN-20260430-001 status draft. Validasi: total debit Rp 2.500.000 = total kredit Rp 2.500.000 ✓ |
| 3 | Budi Manager | Review detail jurnal: GET /api/finance/journals/:id — pastikan akun COA benar dan nominal tepat | Detail jurnal + lines tampil. Akun debit: "6001 Beban Penyusutan", akun kredit: "1601 Akumulasi Penyusutan Peralatan" |
| 4 | Budi Manager | Post jurnal ke GL: POST /api/finance/journals/:id/post | Jurnal status: draft → posted. Ledger entries aktif. Tidak bisa diedit lagi. Periode April harus open (belum ditutup) |
| 5 | Budi Manager | Cek buku besar akun 1601: GET /api/finance/ledger?accountId=<id>&year=2026&month=4 | Ledger menampilkan semua transaksi debit/kredit akun Akumulasi Penyusutan bulan April termasuk jurnal baru |
| 6 | Budi Manager | Verifikasi GL seimbang: GET /api/finance/reports/gl/verify?year=2026&month=4&branchId=<id> | Response: {"balanced":true,"totalDebit":45280000,"totalCredit":45280000} — GL seimbang ✓ |
| 7 | Budi Manager | Sebelum tutup periode, lihat Laporan Laba Rugi draft: GET /api/finance/reports/pnl?year=2026&month=4&branchId=<id> | P&L bulan April tampil: Pendapatan Jasa Rp 38.500.000, HPP Rp 12.200.000, Laba Bersih Rp 21.800.000 (estimasi) |
| 8 | Budi Manager | Tutup periode April: POST /api/finance/periods/close — body: {"branchId":"<uuid>","year":2026,"month":4} | Periode April 2026 → closed. Sistem otomatis buat jurnal penutup: transfer saldo akun nominal ke Laba Ditahan (akun 3002). Tidak ada jurnal baru bisa diposting ke periode ini |
| 9 | Budi Manager | Cetak Laporan Laba Rugi final: GET /api/finance/reports/pnl?year=2026&month=4&branchId=<id> | P&L final April 2026 dengan data lengkap termasuk jurnal penutup |
| 10 | Budi Manager | Cetak Neraca: GET /api/finance/reports/balance-sheet?year=2026&month=4&branchId=<id> | Balance Sheet April 2026 menampilkan Aset, Liabilitas, dan Ekuitas. Total Aset = Total Liabilitas + Ekuitas ✓ |
| 11 | Budi Manager | Export P&L ke Excel untuk dilaporkan: GET /api/finance/reports/pnl/export?year=2026&month=4&branchId=<id>&token=<jwt> | File laba-rugi-2026-04.xlsx terunduh. Format siap kirim ke manajemen pusat |
| 12 | Budi Manager | Export Neraca Saldo ke Excel: GET /api/finance/reports/trial-balance/export?year=2026&month=4&branchId=<id>&token=<jwt> | File neraca-saldo-2026-04.xlsx terunduh. Audit trail lengkap tersedia |