POS & Sales¶
The sales side of the product: ringing up a sale at the till, the shift/station model, tenders, cashier reset/settlement, and credit sales. Selling is where inventory leaves the system, so this ties back to Stock & Costing.
Scope note — the till UI lives elsewhere
This repository contains the POS data-access layer (SmartSoft.POS.FrameWork.DataAccess)
and the back-office consumers of sales data (reset, reporting, delivery orders, credit
sales). The customer-facing touch-screen ring-up terminal UI is a separate product,
not in this checkout. What follows is the sales data model and the back-office flows that
read/close it.
Table relationships¶
erDiagram
tbMaster_Kasir ||--o{ tbTr_Penjualan_H : "cashier rings up"
tbMaster_Customer ||--o{ tbTr_Penjualan_H : "buyer"
tbTr_Penjualan_H ||--|{ tbTr_Penjualan_D : "header / detail"
tbMaster_Barang ||--o{ tbTr_Penjualan_D : "item sold"
tbTr_Penjualan_H ||--o{ tbTr_Piutang : "credit sale"
tbTr_Penjualan_R ||--o{ tbTr_Penjualan_H : "recaps (per shift)"
tbTr_Penjualan_S ||--o{ tbTr_Penjualan_R : "rolls up (per day)"
tbTr_Penjualan_H {
string JLH_KODECABANG PK
date JLH_TGLTRN PK
string JLH_STATION PK
string JLH_SHIFT PK
string JLH_NOSTRUK PK
string JLH_USERID FK "cashier"
string JLH_KODECUST FK
double JLH_GROSS
double JLH_CASH
double JLH_PIUTANG
}
tbTr_Penjualan_D {
string JLD_NOSTRUK PK
int JLD_NOURUT PK
string JLD_PRDCD FK
double JLD_QTY
double JLD_HRGJUAL
double JLD_AVGCOST
}
tbTr_Piutang {
string PIU_NOSTRUK PK
string PIU_KODECUST FK
double PIU_NOMINAL
double PIU_SALDO
}
The sale document¶
A sale is a header + detail pair, tbTr_Penjualan_H / _D.
Station / shift model¶
The sales header key encodes the whole POS hierarchy:
flowchart LR
B["Branch<br/>JLH_KODECABANG"] --> D["Date<br/>JLH_TGLTRN"]
D --> S["Station<br/>JLH_STATION"]
S --> U["Cashier<br/>JLH_USERID"]
U --> SH["Shift<br/>JLH_SHIFT"]
SH --> R["Receipt<br/>JLH_NOSTRUK"]
style R fill:#e8f5e9,stroke:#388e3c
tbTr_Penjualan_H PK = JLH_KODECABANG + JLH_TGLTRN + JLH_STATION + JLH_USERID +
JLH_SHIFT + JLH_TIPETRN + JLH_NOSTRUK. This is the widest key in the schema — it makes
every receipt uniquely addressable by branch, day, till, cashier, and shift.
Tenders (how a sale is paid)¶
A single sale can split across many tender types — each is its own column on the header:
| Column | Tender |
|---|---|
JLH_CASH |
Cash |
JLH_DEBIT (+ JLH_NODEBIT, JLH_KODEBANKDEBIT) |
Debit card |
JLH_CARD (+ JLH_NOCARD, JLH_KODEBANKKREDIT) |
Credit card |
JLH_PIUTANG |
On account (credit sale → receivable) |
JLH_VOUCHER |
Voucher |
JLH_POINT (+ JLH_KODEPOINT, JLH_QTYPOINT) |
Loyalty points |
JLH_TARIKTUNAI / JLH_CASHBACK |
Cash withdrawal / cash back |
JLH_UANGMUKA / JLH_TITIPAN |
Down payment / deposit |
Value & discount columns include JLH_QTY, JLH_HPP (COGS), JLH_GROSS, JLH_PPN,
JLH_DISCOUNTITEM, JLH_DISCOUNTSTRUK, JLH_DISCOUNTVOUCHER, JLH_DISCOUNTCARD,
JLH_DISCOUNTULTAH (birthday), JLH_PEMBULATAN (rounding).
Detail lines¶
tbTr_Penjualan_D PK = JLD_KODECABANG + JLD_NOSTRUK + JLD_NOURUT. Key columns:
JLD_PRDCD (item/PLU), JLD_QTY, JLD_AVGCOST / JLD_LASTCOST (cost captured at sale for
COGS), JLD_HRGJUAL, JLD_GROSS, JLD_PPN, JLD_DISCOUNT, JLD_QTYRETUR,
JLD_PLUHADIAH (gift item from a promo), JLD_TRADEIN.
The in-progress basket lives in tbTemp_Penjualan / tbTemp_Penjualan_D_Member before the
sale is committed.
Receipt printing¶
Receipts print through FrameWork.SmartSoft.Struk/StrukKasir.vb:
StrukKasir()/CreateStrukKasir()and the GDI pathStrukKasirImage()(draws lines viaPrintLine/DrawString,HeaderStruk(),CutStruk(),MaskCC()to mask card numbers).- Header/footer text and paper width come from
PERUSAHAAN(PRSH_HEADSTRUK1..4,PRSH_FOOTER…,PRSH_LBRSTRUK,PRSH_LOGO,PRSH_PRINTERNAME) and parameters (PRM_STRUKDECIMAL,PRM_AUTOCUTTER,PRM_FONTSIZE). - The same file prints the opening-float slip (
StrukKasAwalImage), goods pickup, and DO.
Cashiers are defined in tbMaster_Kasir (KSR_KODEKASIR, KSR_NAMAKASIR, KSR_STATION,
KSR_STATUS, KSR_PASSWORD).
Cashier open → reset → close¶
The shift lifecycle is summarised into progressively higher aggregates by
FrameWork.SmartSoft.BLL/ResetKasir.vb:
flowchart TD
A["Sales rows<br/>tbTr_Penjualan_H/_D"] -->|"Reset() per station/shift"| B["Per-shift reset row<br/>tbTr_Penjualan_R (JLR_*)"]
B -->|"CreateSummary() SUM by branch/date"| C["Daily store summary<br/>tbTr_Penjualan_S (JLS_*)"]
D["ResetAll(): auto-close stale shifts<br/>(JLR_TGLTRN ≠ today, JLR_CLOSING ≠ Y)"] --> B
style B fill:#fff3e0,stroke:#fb8c00
style C fill:#e8f5e9,stroke:#388e3c
| Method | Does |
|---|---|
ResetAll() |
Scans tbTr_Penjualan_R for any station/shift not closed today and resets each — auto-closing stale shifts. |
Reset() |
Aggregates a station/shift/date's sales into the per-shift row tbTr_Penjualan_R (JLR_*): opening float JLR_KASAWAL, JLR_TOTALUANG, times JLR_JAMAWAL/JAMAKHIR, JLR_CLOSING, and sales-vs-return buckets (JLR_SJLH_* / JLR_RJLH_*). |
CreateSummary() |
Rolls the per-shift rows into the daily store summary tbTr_Penjualan_S (JLS_*, incl. JLS_KASAWAL, JLS_SETTUNAI). |
UI: Kasir/frmResetKasir.vb, plus frmSummarySales, frmDailyReport, frmCetakDataReset.
Reset slips print via StrukReset.vb.
Settlement (EDC / card reconciliation)¶
Kasir/frmInputSettlement.vb writes tbTr_Penjualan_Settlement (STL_KODEEDC, STL_TIPE,
STL_NOMINAL, per station/shift), joining tbTabel_EDC (card machines) × tbTabel_JenisBayar
— the operator keys the settled amount per EDC × card type to reconcile against card tenders.
Sales orders & delivery¶
For non-immediate fulfilment, sales orders (Transaksi/SalesOrder/) capture an order,
check/pick it, and cut a delivery order:
flowchart LR
SO["Sales Order<br/>tbTr_SalesOrder_H/_D"] --> CHK["Checker<br/>tbTr_CheckerSO (CEK_*)"]
CHK -->|"ValidasiSO(): QTYSO vs QTYREAL"| SO
SO --> DO["Delivery Order<br/>frmCreateDeliveryOrder"]
style SO fill:#e3f2fd,stroke:#1976d2
tbTr_SalesOrder_H(SOH_NOSO,SOH_NODO,SOH_KODECUST,SOH_QTYSO/SOH_QTYREAL, discounts,SOH_KODESALES) + detailtbTr_SalesOrder_D.FrameWork.SmartSoft.BLL/SALESORDER.vbValidasiSO()reconciles ordered vs checked quantities (tbTr_CheckerSO), writing backSOD_QTYREAL/SOH_QTYREALand stampingCEK_STATUS.
Credit sales & receivables (Piutang)¶
A credit sale (tender JLH_PIUTANG) opens a receivable:
flowchart TD
S["Credit sale (JLH_PIUTANG)"] --> P["tbTr_Piutang<br/>(PIU_NOMINAL, PIU_BAYAR, PIU_SALDO)"]
P --> PAY["Payment: tbTr_BayarPiutang_H/_D<br/>(cash / giro / cheque)"]
P --> CC["Credit control on tbMaster_Customer<br/>CUST_KREDITLIMIT − CUST_SALDOAKHIR"]
style CC fill:#fff3e0,stroke:#fb8c00
- Open A/R —
tbTr_Piutangper receipt (PIU_KODECUST,PIU_NOSTRUK,PIU_NOMINAL,PIU_BAYAR,PIU_SALDO). - Payment —
frmInputPembayaranPiutang→tbTr_BayarPiutang_H(BPH_KODECUST, giro/cheque viaBPH_KODEGIRO/BPH_NOMORGIRO,BPH_NOMINAL) + allocation detailtbTr_BayarPiutang_D(BPD_NOSTRUK,BPD_NOMINAL). - Credit control — on
tbMaster_Customer:CUST_KREDITLIMIT,CUST_TOP(payment term), runningCUST_PIUTANG/CUST_SALDOAKHIR; available credit =CUST_KREDITLIMIT − CUST_SALDOAKHIR.
Customer names are encrypted at rest
CUST_NAMACUST is stored with SQL Server DecryptByPassPhrase('5m@rt5oft', …) — a
hard-coded passphrase in the SQL. Note it as a data-protection weakness alongside the
items on Known Issues.
See Promotions & Loyalty for how discounts, gifts, vouchers, and points attach to a sale.