Purchasing & Approvals¶
How goods get ordered and how changes get signed off. The chain runs min/max → requisition (PB) → purchase order (PO) → goods receipt, with a generic approval workflow gating price, order, min/max, and tag changes.
Table relationships¶
erDiagram
tbTr_UsulanOrderSupplier ||--o{ tbTr_PermintaanBarang_D : "suggests"
tbTr_PermintaanBarang_H ||--|{ tbTr_PermintaanBarang_D : "header / detail"
tbTr_PermintaanBarang_H ||--o{ tbTr_PurchaseOrder_H : "raises PO"
tbTr_PurchaseOrder_H ||--|{ tbTr_PurchaseOrder_D : "header / detail"
tbMaster_Supplier ||--o{ tbTr_PurchaseOrder_H : "vendor"
tbMaster_Barang ||--o{ tbTr_PurchaseOrder_D : "item"
tbMaster_HargaBeli ||--o{ tbTr_PurchaseOrder_D : "buy price"
tbTr_PurchaseOrder_H ||--o{ tbTr_BackOffice_H : "received by (closes)"
tbTr_PurchaseOrder_H {
string POH_KODECABANG PK
string POH_NOPO PK
string POH_NOPB FK
string POH_KODESUP FK
string POH_RECORDID "status ''->1->2/X"
double POH_QTYPO
}
tbTr_PurchaseOrder_D {
string POD_NOPO PK
int POD_NOURUT PK
string POD_PRDCD FK
double POD_QTYPO
double POD_QTYPB "received"
double POD_HRGBELI
}
tbTr_PermintaanBarang_H {
string PBH_KODECABANG PK
string PBH_NOPB PK
string PBH_JENISPB
string PBH_RECORDID
}
The purchasing chain¶
flowchart LR
MM["Min/Max on item<br/>PRD_MINSTOCK / PRD_MAXSTOCK"] -->|"stock ≤ min"| UO["Usulan Order<br/>(suggested order)"]
UO --> PB["Permintaan Barang (PB)<br/>tbTr_PermintaanBarang_H/D"]
PB --> PO["Purchase Order (PO)<br/>tbTr_PurchaseOrder_H/D"]
PO --> RCV["Goods Receipt (Penerimaan)<br/>tbTr_BackOffice_H/D + stock"]
RCV -->|"closes"| PO
style UO fill:#e3f2fd,stroke:#1976d2
style RCV fill:#e8f5e9,stroke:#388e3c
1. Suggested order (Usulan Order), driven by min/max¶
Automatic replenishment (frmProsesPBOtomatis.vb → ProsesPB()):
- Selects items where
PRD_QTYSTOCK <= PRD_MINSTOCK, not a bundle (PRD_PAKET<>'Y'), and flagged auto-order (tbMaster_Tag.TAG_AUTOORDER='Y'); honours the company PB schedulePRSH_JADWALPB. - Order qty =
PRD_MAXSTOCK − PRD_QTYSTOCK − PB_HOLD, wherePB_HOLD = Σ(POD_QTYPO − POD_QTYPB)over open POs, rounded to multiples ofPRD_MINOR. - Writes
tbTr_PermintaanBarang_H/D(PBH_JENISPB="B", keterangan "PB OTOMATIS").
The suggestion is queued at supplier level in tbTr_UsulanOrderSupplier (UOS_PRDCD,
UOS_QTY, UOS_TGLORDER/UOS_TGLPB/UOS_TGLPO, UOS_RECORDID) and, for warehouse
proposals, tbTr_UsulanOrderGudang (UOG_KODELOK1→UOG_KODELOK2, UOG_QTY). Manual
over-max suggestions raise approval "004".
On UOH_/UOD_ naming
Some forms refer to a tbTr_UsulanOrder_H/_D (UOH_/UOD_) header/detail, but there is
no typed data-access class for it — the verified proposal tables are the two
UOS_/UOG_ ones above. Treat UOS_/UOG_ as authoritative.
2. Requisition (Permintaan Barang, PB)¶
tbTr_PermintaanBarang_H/D:
| Level | Key columns |
|---|---|
Header PBH_ |
NOPB (PK), TGLPB, JENISPB ("B" = buy/PO-bound), QTYPB, RECORDID, NOUO, NOPO, inter-branch transfer fields |
Detail PBD_ |
NOPB, NOURUT, PRDCD, QTYPB, HRGBELI, KODESUP, RECORDID |
Entry is manual (frmInputPermintaanBarang.vb) or automatic (above). PBD_RECORDID='1' /
PBH_RECORDID='1' mark lines converted to a PO.
3. Purchase order (PO)¶
tbTr_PurchaseOrder_H/D. Raised by ProsesPO() from an entered PB:
- Doc number via
HAKFUNC.GetNomor("PO-<branch>-<yyyyMM>-", 5). - Rejects any PB line lacking a buy price in
tbMaster_HargaBeli. - Groups PB detail by supplier +
PRD_DIVISIPO+PRD_BKP, splitting to a new PO everyPRM_MAXIMUMITEMPOitems; pulls price/PPN/discount/bonus fromtv_Data_HargaBeli. - Advances the supplier queue (
UOS_RECORDID2→3), updatesPRD_LASTPO, printsBuktiPurchaseOrder.
stateDiagram-v2
[*] --> New: POH_RECORDID = ''
New --> Sent: '1' (sent to branch/supplier)
Sent --> Received: '2' (fully received)
Sent --> Cancelled: 'X' (receiving cancelled)
Received --> [*]
note right of Sent
Open line = POD_QTYPO > POD_QTYPB
end note
Header key columns (POH_): NOPO (PK), TGLPO, NOPB, KODESUP, KODECABANGUO,
QTYPO, GROSS, PPN, TOP, STATUS, RECORDID. Detail (POD_): NOPO, NOURUT,
PRDCD, QTYPO, QTYPB (qty received against the line), HRGBELI, AVGSALES,
LASTPO, RECORDID.
4. Goods receipt closes the PO¶
Receiving (frmInputPenerimaanBarang.vb, and the posting Class/PenerimaanBarang.vb)
matches the PO by POH_NOPO, receives into tbTr_BackOffice_H/D + tbTr_Receiving, and
updates stock/cost (see Transactions). On completion it sets
POD_RECORDID='1'; when no open line remains it sets POH_RECORDID='2' and back-fills
POH_NOPB with the receipt (BTB) number. Outstanding POs are viewed via
tv_DataOutstandingPO (frmTransaksiOutstandingPO.vb).
The approval workflow engine¶
A single generic engine routes several kinds of change (prices, orders, min/max, tags) through configurable multi-level sign-off.
flowchart TD
subgraph Config["Config"]
WF["tbMaster_WorkFlowApproval (WFL_)<br/>APPR_ID + LEVEL → USERID list"]
AT["tbTabel_Approval<br/>(APPR_ID → name)"]
end
subgraph Queue["Runtime"]
PND["tbTr_PendingApproval (PND_)<br/>APPR_ID + LEVEL waiting"]
PROP["Proposal rows (*_RECORDID = current level)"]
end
Config --> Queue
PROP -->|"approve at level N"| CHK{"Next level exists?<br/>(WFL_LEVEL = N+1)"}
CHK -->|Yes| BUMP["RECORDID → N+1; ensure PND row at N+1"]
CHK -->|No, final| APPLY["Apply change to live master;<br/>delete PND row"]
style APPLY fill:#e8f5e9,stroke:#388e3c
| Piece | Role |
|---|---|
tbMaster_WorkFlowApproval (WFL_) |
Config: KODECABANG+APPR_ID+LEVEL → WFL_USERID (semicolon-delimited approver list). Maintained in frmMasterWorkFlow.vb. |
tbTabel_Approval |
Lookup of approval types (APPR_ID → APPR_NAME). |
tbTr_PendingApproval (PND_) |
The queue: a row means work of this type is waiting at this level. |
Routing mechanism (identical across the four Usulan forms): the menu launcher encodes
"APPR_ID-LEVEL" into the form's AccessibleName; the form splits it, loads proposal rows
whose *_RECORDID equals the current level, and on approve either applies the change to
the live master (if it is the final level) or bumps the row to the next level and
ensures a tbTr_PendingApproval row exists there. When the grid empties, the PND row for
that level is deleted.
Approval IDs seen: 001 Harga Jual Usulan (price, raised on goods receipt), 002/003
(buy price / mutation), 004 Usulan Order over-max. Forms: frmHargaJualUsulan,
frmMinMaxStockUsulan, frmTagUsulan, frmUsulanOrder, frmApprovalTargetSales.
Task list is separate from approvals
tbTabel_TaskList (TASK_SQLQUERY, TASK_CRITERIA, TASK_SOUND, TASK_LEADTIME)
drives user-defined SQL alerts/reminders on a dashboard — a different mechanism from
the PND approval queue.
Approval & pricing relationships¶
erDiagram
tbMaster_WorkFlowApproval ||--o{ tbTr_PendingApproval : "defines levels"
tbMaster_HargaJualUsulan }o--o{ tbTr_PendingApproval : "queued (APPR 001)"
tbMaster_MinMaxStockUsulan }o--o{ tbTr_PendingApproval : "queued (APPR)"
tbMaster_TagUsulan }o--o{ tbTr_PendingApproval : "queued (APPR)"
tbMaster_HargaJualUsulan ||--o| tbMaster_HargaJual : "final approve -> live"
tbMaster_TagUsulan ||--o| tbTr_PerubahanTag : "final approve -> live"
tbMaster_WorkFlowApproval {
string WFL_KODECABANG PK
string WFL_APPR_ID PK
int WFL_LEVEL PK
string WFL_USERID "approver list (;)"
}
tbTr_PendingApproval {
string PND_KODECABANG PK
string PND_APPR_ID PK
int PND_LEVEL PK
}
tbMaster_HargaJualUsulan {
string HGJU_PRDCD PK
string HGJU_NODOC PK
string HGJU_RECORDID "current level"
double HGJU_HRGJUALUSULAN
string HGJU_APPROVALBY
}
Pricing¶
Prices are versioned and (for selling price) approval-gated.
flowchart LR
subgraph Sell["Selling price"]
RCV["Goods receipt:<br/>cost changed?"] -->|"PRM_HRGJUALUSULAN=Y"| PROP["tbMaster_HargaJualUsulan (HGJU_)<br/>= cost × (1 + markup)"]
PROP -->|"approval 001"| LIVE["tbMaster_HargaJual (HGJ_)<br/>+ PRD_HRGJUAL, PRD_HRGJUALOLD"]
end
subgraph Buy["Buy price"]
HB["tbMaster_HargaBeli (HGB_)"] --> HIST["tbHist_HargaBeli (HHGB_)<br/>old → new, supplier change"]
end
style PROP fill:#fff3e0,stroke:#fb8c00
style LIVE fill:#e8f5e9,stroke:#388e3c
| Concern | Live | Proposal | History |
|---|---|---|---|
| Selling price | tbMaster_HargaJual (HGJ_, RECORDID ''=future / '1'=active); mirror PRD_HRGJUAL/PRD_HRGJUALOLD; wholesale tbMaster_HargaGrosir |
tbMaster_HargaJualUsulan (HGJU_) → approval 001 (blocked for items on active promo) |
— |
| Buy price | tbMaster_HargaBeli (HGB_, disc/bonus tiers) |
— | tbHist_HargaBeli (HHGB_, old/new price + supplier), written from price edits and receipts |
| Min/Max | PRD_MINSTOCK/PRD_MAXSTOCK/PRD_MINOR; master tbMaster_MinMaxStock |
tbMaster_MinMaxStockUsulan (MINU_) → approval |
— |
| Price tag | tbMaster_Tag (TAG_AUTOORDER); item PRD_KODETAG |
tbMaster_TagUsulan (TAGU_) → approval → tbTr_PerubahanTag |
— |
Selling-price proposals are auto-created on goods receipt when landed cost changes:
Class/PenerimaanBarang.vb writes tbHist_HargaBeli and, if PRM_HRGJUALUSULAN="Y",
creates a tbMaster_HargaJualUsulan at cost × (100 + PRD_MARKUP)/100 (rounded by
PRM_PEMBULATANHRGJUAL) and raises approval 001. This is the link between
Transactions and this page: receiving stock can trigger a
pricing approval.