Lewati ke konten utama

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

AktorDeskripsi
KasirUser dengan akses POS — melakukan entry transaksi, pembayaran, cetak resi
AdministratorMengatur konfigurasi POS, hak akses user, dan produk yang tampil di katalog
SystemBackend 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

IDUse CaseAktorPre-ConditionPost-Condition
UC-01Akses Dashboard POSKasirUser login dengan hak akses POSHalaman POS Dashboard ditampilkan
UC-02Tambah Produk ke CartKasirBerada di POS Transaction EntryProduk masuk ke cart
UC-03Kelola CartKasirMinimal 1 item di cartCart diperbarui sesuai aksi
UC-04Checkout & Review OrderKasirMinimal 1 item di cartHalaman checkout ditampilkan
UC-05Proses PembayaranKasirBerada di halaman checkoutPembayaran dikonfirmasi
UC-06Cetak Resi StrukKasirPembayaran berhasil, SO terbuatResi tercetak / tampil
UC-07Kelola Produk POSAdministratorLogin sebagai administratorFlag pos_available diperbarui
UC-08Konfigurasi Sistem POSAdministratorLogin sebagai administratorSystem setting POS tersimpan
UC-09Generate Sales OrderSystemPembayaran dikonfirmasi kasirSO 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

#IssueStatus
1Nama tabel product master aktual di schema DB perlu dikonfirmasi (pt_mstr atau nama lain)Open
2Numbering SO dari POS — prefix berbeda atau ikut sequence existing?Open
3Integrasi payment gateway real (Midtrans) dijadwalkan untuk phase berapa?Deferred to Phase 2
4Resi via WhatsApp / emailOut of Scope Phase 1

Referensi