Mini POS — Analysis Design & Solution Modeling
Tujuan Dokumen
Dokumen ini berisi analisis desain dan pemodelan solusi untuk fitur Mini Point of Sales (POS) pada TessaERP.
Merupakan kelanjutan dari mini-pos-brd.md dan mencakup use case, flowchart, component diagram, sequence diagram, dan schema data.
Use Case
Aktor
| Aktor | Deskripsi |
|---|---|
| Kasir | User dengan akses POS — melakukan entry transaksi, pembayaran, cetak resi |
| Administrator | Mengatur konfigurasi POS, hak akses user, dan produk yang tampil di katalog |
| System | Backend TessaERP — memproses transaksi dan menghasilkan Sales Order |
Use Case Diagram
graph TD
Kasir((Kasir))
Admin((Administrator))
System((System))
UC01[UC-01: Akses Dashboard POS]
UC02[UC-02: Tambah Produk ke Cart]
UC02a[UC-02a: via Scan Barcode]
UC02b[UC-02b: via Search SKU / Nama]
UC02c[UC-02c: via Katalog Bergambar]
UC03[UC-03: Kelola Cart]
UC04[UC-04: Checkout & Review Order]
UC05[UC-05: Proses Pembayaran]
UC05a[UC-05a: Cash - Hitung Kembalian]
UC05b[UC-05b: Mock Payment Gateway]
UC06[UC-06: Cetak Resi Struk]
UC07[UC-07: Kelola Produk POS]
UC08[UC-08: Konfigurasi Sistem POS]
UC09[UC-09: Generate Sales Order]
Kasir --> UC01
Kasir --> UC02
UC02 --> UC02a
UC02 --> UC02b
UC02 --> UC02c
Kasir --> UC03
Kasir --> UC04
Kasir --> UC05
UC05 --> UC05a
UC05 --> UC05b
Kasir --> UC06
Admin --> UC07
Admin --> UC08
System --> UC09
UC05 --> UC09
Detail Use Case
| ID | Use Case | Aktor | Pre-Condition | Post-Condition |
|---|---|---|---|---|
| UC-01 | Akses Dashboard POS | Kasir | User login dengan hak akses POS | Halaman POS Dashboard ditampilkan |
| UC-02 | Tambah Produk ke Cart | Kasir | Berada di POS Transaction Entry | Produk masuk ke cart |
| UC-03 | Kelola Cart | Kasir | Minimal 1 item di cart | Cart diperbarui sesuai aksi |
| UC-04 | Checkout & Review Order | Kasir | Minimal 1 item di cart | Halaman checkout ditampilkan |
| UC-05 | Proses Pembayaran | Kasir | Berada di halaman checkout | Pembayaran dikonfirmasi |
| UC-06 | Cetak Resi Struk | Kasir | Pembayaran berhasil, SO terbuat | Resi tercetak / tampil |
| UC-07 | Kelola Produk POS | Administrator | Login sebagai administrator | Flag pos_available diperbarui |
| UC-08 | Konfigurasi Sistem POS | Administrator | Login sebagai administrator | System setting POS tersimpan |
| UC-09 | Generate Sales Order | System | Pembayaran dikonfirmasi kasir | SO terbuat dengan source_transaction = 'POS' |
Flowchart
Alur Utama Transaksi POS
flowchart TD
A([Kasir Login]) --> B[Akses Menu POS\n/handheld/point-of-sales]
B --> C[POS Dashboard]
C --> D[klik + Tambah Entry Transaksi Baru]
D --> E[POS Transaction Entry]
E --> F{Cara tambah produk?}
F --> F1[Scan Barcode\nproduk langsung masuk cart]
F --> F2[Search SKU / Nama\npilih dari hasil]
F --> F3[Klik Katalog\npilih dari grid card]
F1 & F2 & F3 --> G[Kelola Cart\n+/- qty, hapus, catatan per item]
G --> H{Cart kosong?}
H -- Ya --> E
H -- Tidak --> I[klik → Lanjutkan Order]
I --> J[Shopping Cart & Checkout\nReview item, subtotal, diskon, tax, grand total]
J --> K{Aksi kasir?}
K -- Kembali --> E
K -- Lanjut --> L[Payment Processing]
L --> M{Metode Pembayaran}
M --> N[Cash\ninput nominal diterima\ntampilkan kembalian]
M --> O[Mock Payment Gateway\nkonfirmasi simulasi]
N --> P{nominal ≥ grand total?}
P -- Tidak --> N
P -- Ya --> Q
O --> Q[klik Simpan & Cetak Resi]
Q --> R[POST /api/pos/transaction]
R --> S[Generate Sales Order\nsource_transaction=POS\nstatus=Approved]
S --> T[Tampilkan Resi Struk]
T --> C
Flowchart Validasi Backend
flowchart TD
A([Terima POST /api/pos/transaction]) --> B{Minimal 1 item?}
B -- Tidak --> B1([Return 400: items required])
B -- Ya --> C{pos_grandtotal > 0?}
C -- Tidak --> C1([Return 400: invalid amount])
C -- Ya --> D{Payment type = Cash?}
D -- Ya --> E{pos_payment_cash\n≥ pos_grandtotal?}
E -- Tidak --> E1([Return 400: insufficient cash])
E -- Ya --> F
D -- Tidak --> F{Idempotency key\nduplikat?}
F -- Ya --> F1([Return 200: SO existing])
F -- Tidak --> G[Buat Sales Order]
G --> H[Set source_transaction=POS\norder_type=POS_CASH\nstatus=Approved]
H --> I([Return 200: SO created])
Mockup
Mockup interaktif tersedia di folder yang sama: mini-pos-mockup.html (planned)
Referensi wireframe: mini-pos-brd.md
Component Diagram
graph TD
subgraph Neuron_ERP["Neuron_ERP — Blazor Server"]
subgraph POS_Pages["Pages / POS"]
P1[POSDashboard.razor\n/pos]
P2[POSTransactionEntry.razor\n/handheld/point-of-sales]
P3[POSCheckout.razor]
P4[POSPayment.razor]
P5[POSReceipt.razor\nmodal printout]
end
subgraph POS_API["Controllers / POS"]
C1["GET /api/pos/products"]
C2["POST /api/pos/transaction"]
C3["GET /api/pos/config"]
end
P2 --> C1
P4 --> C2
P1 --> C3
end
subgraph NeuronLibrary["NeuronLibrary — Business Logic"]
subgraph POS_Service["Data / Sales Order / POS"]
S1[IPOSService.cs]
S2[POSService.cs\nDapper + SqlHelper]
end
subgraph POS_Models["Models / Sales Order / POS"]
M1[POSProductModel.cs]
M2[POSCartItemModel.cs]
M3[POSTransactionModel.cs]
M4[POSConfigModel.cs]
M5[POSReceiptModel.cs]
end
S2 --> M1
S2 --> M3
end
subgraph DB["PostgreSQL Database"]
T1[(sales_order\n+kolom POS)]
T2[(so_det)]
T3[(pt_mstr\n+pos_available)]
T4[(pos_system_config)]
end
C1 --> S1
C2 --> S1
C3 --> S1
S1 --> S2
S2 --> T1
S2 --> T2
S2 --> T3
S2 --> T4
Sequence Diagram
UC-02: Tambah Produk via Scan Barcode
sequenceDiagram
actor Kasir
participant Entry as POSTransactionEntry.razor
participant API as POSController
participant Svc as POSService
participant DB as PostgreSQL
Kasir->>Entry: scan barcode
Entry->>API: GET /api/pos/products?search={barcode}
API->>Svc: GetProductsAsync(search, entityId)
Svc->>DB: SELECT WHERE barcode=? AND pos_available=TRUE AND status=active
DB-->>Svc: product row
Svc-->>API: POSProductModel
API-->>Entry: [POSProductModel]
Entry->>Entry: tambah ke cart state (local)
Entry-->>Kasir: cart diperbarui
UC-05 / UC-09: Proses Pembayaran & Generate SO
sequenceDiagram
actor Kasir
participant Pay as POSPayment.razor
participant API as POSController
participant Svc as POSService
participant DB as PostgreSQL
Kasir->>Pay: klik [Simpan & Cetak Resi]
Pay->>API: POST /api/pos/transaction\nHeader: Idempotency-Key: {uuid}
API->>API: validasi payload
API->>Svc: CheckIdempotencyKey(key)
Svc->>DB: SELECT idempotency_key
DB-->>Svc: not found
Svc-->>API: not duplicate
API->>Svc: CreateSOFromPOS(POSTransactionModel)
Svc->>DB: INSERT so_mstr\n(source_transaction=POS, status=Approved)
Svc->>DB: INSERT so_det (item-item)
DB-->>Svc: so_id, so_number
Svc-->>API: {sales_order_id, sales_order_number}
API-->>Pay: 200 OK {so_id, so_number, status}
Pay-->>Kasir: tampilkan modal resi struk
Schema Data
Perubahan Tabel sales_order
ALTER TABLE sales_order
ADD COLUMN IF NOT EXISTS additional_property_1 VARCHAR(500),
ADD COLUMN IF NOT EXISTS additional_property_2 VARCHAR(100),
ADD COLUMN IF NOT EXISTS pos_payment_type VARCHAR(50),
ADD COLUMN IF NOT EXISTS pos_grandtotal DECIMAL(18,2),
ADD COLUMN IF NOT EXISTS pos_payment_cash DECIMAL(18,2),
ADD COLUMN IF NOT EXISTS pos_payment_change DECIMAL(18,2),
ADD COLUMN IF NOT EXISTS pos_subtotal DECIMAL(18,2),
ADD COLUMN IF NOT EXISTS pos_discount DECIMAL(18,2),
ADD COLUMN IF NOT EXISTS pos_tax_percent DECIMAL(5,2),
ADD COLUMN IF NOT EXISTS pos_tax_amount DECIMAL(18,2),
ADD COLUMN IF NOT EXISTS pos_tax_include_exclude VARCHAR(10),
ADD COLUMN IF NOT EXISTS source_transaction VARCHAR(20);
Perubahan Tabel Product Master
ALTER TABLE pt_mstr
ADD COLUMN IF NOT EXISTS pos_available BOOLEAN NOT NULL DEFAULT FALSE;
CREATE INDEX IF NOT EXISTS idx_pt_mstr_pos_available
ON pt_mstr (pos_available)
WHERE pos_available = TRUE;
Tabel Konfigurasi POS
CREATE TABLE IF NOT EXISTS pos_system_config (
psc_id SERIAL PRIMARY KEY,
psc_en_id INT NOT NULL,
psc_key VARCHAR(100) NOT NULL,
psc_value TEXT,
psc_updated_at TIMESTAMP DEFAULT NOW(),
UNIQUE (psc_en_id, psc_key)
);
Entity Relationship Diagram
erDiagram
pt_mstr {
int pt_id PK
string pt_code
string pt_desc1
string barcode
bool pos_available
bool pt_active
}
so_mstr {
int so_id PK
string so_no
int so_ptnr_id_sold
string so_trans_id
string source_transaction
string order_type
string pos_payment_type
decimal pos_grandtotal
decimal pos_payment_cash
decimal pos_payment_change
decimal pos_subtotal
decimal pos_discount
decimal pos_tax_percent
decimal pos_tax_amount
string pos_tax_include_exclude
string additional_property_1
string additional_property_2
}
so_det {
int sod_id PK
int sod_so_oid FK
int sod_pt_id FK
decimal sod_qty
decimal sod_unit_price
decimal sod_discount
string sod_notes
}
pos_system_config {
int psc_id PK
int psc_en_id
string psc_key
string psc_value
}
so_mstr ||--o{ so_det : "has"
pt_mstr ||--o{ so_det : "ordered in"
Model / DTO Utama (C#)
// Models/Sales Order/POS/POSProductModel.cs
public class POSProductModel
{
public string ProductCode { get; set; }
public string ProductName { get; set; }
public string Sku { get; set; }
public string Barcode { get; set; }
public decimal UnitPrice { get; set; }
public decimal AvailableStock { get; set; }
public string ImageUrl { get; set; }
public string Notes { get; set; }
}
// Models/Sales Order/POS/POSTransactionModel.cs
public class POSTransactionModel
{
public string CustomerName { get; set; }
public string PhoneNumber { get; set; }
public string TableNumber { get; set; }
public string HeaderNotes { get; set; }
public List<POSCartItem> Items { get; set; }
public string PosPaymentType { get; set; } // cash/debit/qris/ewallet
public decimal PosSubtotal { get; set; }
public decimal PosDiscount { get; set; }
public decimal PosTaxPercent { get; set; }
public decimal PosTaxAmount { get; set; }
public string PosTaxIncludeExclude { get; set; } // include/exclude
public decimal PosGrandtotal { get; set; }
public decimal PosPaymentCash { get; set; }
public decimal PosPaymentChange { get; set; }
}
public class POSCartItem
{
public string ProductId { get; set; }
public decimal Qty { get; set; }
public decimal UnitPrice { get; set; }
public decimal Discount { get; set; }
public string Notes { get; set; }
}
// Models/Sales Order/POS/POSConfigModel.cs
public class POSConfigModel
{
public decimal TaxPercent { get; set; }
public string TaxIncludeExclude { get; set; }
public string DefaultCostCenter { get; set; }
public string ReceiptHeader { get; set; }
public string ReceiptFooter { get; set; }
public bool TableNumberEnabled { get; set; }
}
Open Issues
| # | Issue | Status |
|---|---|---|
| 1 | Nama tabel product master aktual di schema DB perlu dikonfirmasi (pt_mstr atau nama lain) | Open |
| 2 | Numbering SO dari POS — prefix berbeda atau ikut sequence existing? | Open |
| 3 | Integrasi payment gateway real (Midtrans) dijadwalkan untuk phase berapa? | Deferred to Phase 2 |
| 4 | Resi via WhatsApp / email | Out of Scope Phase 1 |
Referensi
- BRD:
mini-pos-brd.md - BRD Asli (SharePoint): BRD_POS.docx