Skip to content

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-PRDCD discount overrides; tbMaster_Promosi_D_P2P holds 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.