Promotions & Loyalty¶
The system has a rich promotions engine plus loyalty points and vouchers. All three attach to a POS sale (see POS & Sales) and are logged per receipt so they can be reported and reconciled.
Table relationships¶
erDiagram
tbMaster_Promosi_H ||--|{ tbMaster_Promosi_D : "header / detail"
tbMaster_Promosi_H ||--o{ tbTr_Promosi : "applied as"
tbTr_Penjualan_H ||--o{ tbTr_Promosi : "on receipt"
tbMaster_Customer ||--o{ tbTr_Promosi : "for"
tbMaster_Customer ||--o{ tbMaster_StockPoint : "point balance"
tbMaster_StockPoint ||--o{ tbTr_PointRewards : "ledger"
tbTr_Voucher ||--o{ tbMaster_Voucher : "issues range"
tbMaster_Voucher ||--o{ tbTr_Voucher_D : "redeemed at POS"
tbTr_Penjualan_H ||--o{ tbTr_Voucher_D : "on receipt"
tbMaster_Promosi_H {
string PRMH_KODECABANG PK
string PRMH_KODEPROMOSI PK
string PRMH_TIPEPROMOSI
string PRMH_JENISDISCOUNT
string PRMH_PRDCDHADIAH "gift"
}
tbTr_Promosi {
string TRP_KODEPROMOSI PK
string TRP_NOSTRUK PK
string TRP_PRDCD PK
double TRP_DISCOUNT
string TRP_PRDCDHADIAH
double TRP_POINTREWARD
}
tbMaster_StockPoint {
string STP_KODECUST PK
string STP_KODEPOINT PK
double STP_SALDOAKHIR
}
tbMaster_Voucher {
string VCR_NOVOUCHER PK
double VCR_NILAIVOUCHER
string VCR_RECORDID "blank=avail / 1=used"
date VCR_TGLEXPIRED
}
Promotions¶
Where promos are defined¶
flowchart LR
subgraph Master["Definition (frmPromosi.vb)"]
H["tbMaster_Promosi_H (PRMH_*)<br/>rules, tiers, validity, scope"]
D["tbMaster_Promosi_D (PRMD_*)<br/>per-item overrides"]
P2P["tbMaster_Promosi_D_P2P<br/>purchase-with-purchase prices"]
end
subgraph Runtime["Runtime"]
A["tbMaster_Promosi_A (PRMA_*)<br/>flattened H+D join"]
E["BLL PROMOSI.vb"]
end
Master --> A --> E
E --> LOG["tbTr_Promosi (TRP_*)<br/>applied promos per receipt"]
style E fill:#e8eaf6,stroke:#3f51b5
style LOG fill:#e8f5e9,stroke:#388e3c
tbMaster_Promosi_H(PRMH_*) is wide: promo type, discount type, validity window (PRMH_HARI,PRMH_JAMAWAL/JAMAKHIR,PRMH_MAXHARI/MAXPERIODE), scope (PRMH_GROUPCABANG,PRMH_GROUPCUSTOMER,PRMH_TYPECUSTOMER), tiered buy/discount ladders (PRMH_QTYBELI..5,PRMH_RPHBELI..5,PRMH_PERDISC..5,PRMH_RPHDISC..5), struk bounds (PRMH_MINSTRUK/MAXSTRUK), gifts (PRMH_PRDCDHADIAH..5), voucher, purchase-with-purchase (PRMH_PRDCDPURCHASE/PRMH_HRGPURCHASE), cashback, point reward, redeem-point, card conditions (PRMH_KODEBANK/PRMH_TYPECARD), and combinability flags (PRMH_PROMOSITUNGGAL/PROMOSITAMBAHAN/TANPADISCOUNT).tbMaster_Promosi_D(PRMD_*) holds per-PRDCDdiscount overrides;tbMaster_Promosi_D_P2Pholds the purchase-to-purchase price list.- At runtime the engine reads the flattened
tbMaster_Promosi_A(PRMA_*, header+detail joined).
The runtime engine (BLL/PROMOSI.vb)¶
| Method | Scope | Behaviour |
|---|---|---|
getPromosiItem() |
Item-level (PRMA_TIPEPROMOSI='0') |
Matches on PRMA_PRDCD; checks single/combinable flags, min-struk, card conditions, and JamPromosi(...) (day/time/branch/customer/max-usage window); computes discount (%/Rp with extra tier), gift (vPLUHADIAH via GetPLUHadiah), voucher, and point reward — honouring PRMA_KELIPATAN multiples. |
getPromosiStruk() |
Whole-receipt (PRMA_TIPEPROMOSI≠'0') |
Switches on PRMA_JENISDISCOUNT. |
getPromosiPurcaseToPurchase() |
P2P | Returns the purchase-to-purchase item table. |
Whole-receipt promo types (PRMA_JENISDISCOUNT):
| Code | Type |
|---|---|
0 |
Discount Harga (price discount) |
1 |
Hadiah Langsung (direct gift) |
2 |
Voucher (P2P) |
3 |
Purchase-to-Purchase (PRMA_PRDCDPURCHASE at PRMA_HRGPURCHASE) |
4 |
CashBack |
5 |
Point Reward |
Applied promos are logged¶
Every promo applied to a receipt is written to tbTr_Promosi (TRP_*): TRP_KODEPROMOSI,
TRP_NOSTRUK, TRP_KODECUST, TRP_JENISDISCOUNT, TRP_PRDCD, TRP_DISCOUNT, TRP_VOUCHER,
TRP_PRDCDHADIAH (awarded gift item), TRP_CASHBACK, TRP_POINTREWARD,
TRP_REEDEMPOINT, TRP_KELIPATAN. This log is what stock reconciliation reads to move gift
items — see the sales branch of Stock & Costing.
Loyalty points¶
Points are a per-customer balance ledger, run by BLL/POINTREWARD.vb.
flowchart LR
EARN["Earn (TIPE=IN)<br/>+STP_MASUK"] --> BAL["tbMaster_StockPoint (STP_*)<br/>SALDOAWAL · MASUK · KELUAR · SALDOAKHIR"]
REDEEM["Redeem<br/>+STP_KELUAR"] --> BAL
BAL -->|"ClosingStockPoint(): period close"| HIST["tbHist_StockPoint<br/>(SALDOAWAL = SALDOAKHIR, buckets → 0)"]
style BAL fill:#e8f5e9,stroke:#388e3c
style HIST fill:#ede7f6,stroke:#673ab7
| Method | Does |
|---|---|
UpdateStockPoint() |
Upserts the customer balance in tbMaster_StockPoint (STP_KODECUST, STP_KODEPOINT, STP_SALDOAWAL/MASUK/KELUAR/SALDOAKHIR). TIPE="IN" earns (→STP_MASUK); otherwise redeems (→STP_KELUAR). |
ClosingStockPoint() |
Period close — archives to tbHist_StockPoint (keyed STP_PERIODE) then rolls STP_SALDOAWAL = STP_SALDOAKHIR and zeroes the buckets. (Same roll-forward shape as stock closing.) |
Supporting tables: tbMaster_PointReward (POIN_KODEPOINT, POIN_NILAIPOINT,
POIN_EXPIRED), ledger tbTr_PointRewards (TPR_KODECUST, TPR_NOSTRUK, TPR_REWARDS,
TPR_PEMAKAIAN), and a parallel generic-rewards set (tbMaster_StockRewards,
tbTr_Rewards). Points move as a tender through JLH_POINT/JLH_KODEPOINT/
JLH_QTYPOINT, and are earned via promos (TRP_POINTREWARD).
Vouchers¶
flowchart LR
ISS["Issue (frmInputVoucher)<br/>tbTr_Voucher (VCH_*)"] -->|"explode number range"| VCR["tbMaster_Voucher (VCR_*)<br/>one row per voucher no."]
VCR -->|"redeem at POS"| RED["tbTr_Voucher_D (VCD_*)<br/>→ JLH_VOUCHER"]
style VCR fill:#e3f2fd,stroke:#1976d2
| Stage | Table / detail |
|---|---|
| Issue | frmInputVoucher.vb writes tbTr_Voucher (VCH_TIPE = M issue / K void, VCH_NOAWAL/VCH_NOAKHIR range, VCH_NILAI, validity dates), then explodes the number range into individual tbMaster_Voucher rows (VCR_NOVOUCHER, VCR_NILAIVOUCHER, VCR_MINBELI, VCR_TGLEXPIRED). VCR_RECORDID blank = available, '1' = used/void. Slip via StrukService.CetakVoucher / BuktiPenerimaanVoucher.rpt. |
| Redeem | Recorded per receipt in tbTr_Voucher_D (VCD_NOVOUCHER, VCD_NILAIVOUCHER); the amount lands in JLH_VOUCHER / JLH_DISCOUNTVOUCHER. |
| Expiry | Per-voucher dates (VCR_TGLEXPIRED); the default span is the company parameter PRSH_VOUCHEREXPIRED (days). |
One receipt, three benefit types
A single sale can carry a price discount and award a gift, points, cashback, and consume
a voucher — each captured separately (TRP_* for promos, TPR_*/STP_* for points,
VCD_* for vouchers) and reflected in the header tender/discount columns. When a total
looks off, reconcile these logs against the header before suspecting the till.