Transactions & Posting¶
Almost every inventory movement — receipts, returns, transfers, disposals, adjustments — shares one generic posting mechanism: a draft header/detail pair is copied into a posted header/detail pair, discriminated by a document-type code, and then applied to stock. Learn this once and every transaction reads the same way.
The two table pairs¶
flowchart LR
subgraph Draft["Draft / staging"]
WH["tbTr_BoTrans_H"]
WD["tbTr_BoTrans_D"]
end
subgraph Posted["Posted / committed"]
PH["tbTr_BackOffice_H"]
PD["tbTr_BackOffice_D"]
end
WH -->|"INSERT … SELECT *"| PH
WD -->|"INSERT … SELECT *"| PD
Draft -->|"DELETE after copy"| X["(draft cleared)"]
PH --> STK["UpdateStock() via cSTOK"]
style Draft fill:#fff3e0,stroke:#fb8c00
style Posted fill:#e8f5e9,stroke:#388e3c
tbTr_BoTrans_H/_D— the draft header + detail, where a document is entered and edited on the input form.tbTr_BackOffice_H/_D— the posted (permanent) header + detail.
Both pairs have identical schemas, which is why posting can copy with SELECT *. The
header column BOH_TIPEDOC (a single character) says what kind of document it is.
The posting flow¶
Each transaction class (PenerimaanBarang, ReturBarang, …) exposes a Proses…()
method that builds an ArrayList of SQL statements and runs them as one batch via
HAKDAC.NonQuery:
sequenceDiagram
autonumber
participant Frm as Input form
participant Cls as Transaction class
participant Dac as HAKDAC
participant Stk as cSTOK (STOCK)
participant DB as SQL Server
Frm->>Cls: Proses…(NoDoc, Tanggal, Cabang, Lokasi)
Cls->>DB: INSERT tbTr_BackOffice_H SELECT * FROM tbTr_BoTrans_H WHERE NODOC=…
Cls->>DB: UPDATE tbTr_BackOffice_H SET BOH_TGLDOC=…
Cls->>DB: INSERT tbTr_BackOffice_D SELECT * FROM tbTr_BoTrans_D WHERE NODOC=…
Cls->>DB: DELETE tbTr_BoTrans_H / _D (clear draft)
Dac-->>Cls: success?
Cls->>Cls: UpdateStock()
loop each detail row
Cls->>Stk: set movement buckets + GetAvgCost
Stk->>DB: UPDATE tbMaster_Stock / tbMaster_Barang
end
Steps:
INSERT INTO tbTr_BackOffice_H SELECT * FROM tbTr_BoTrans_H WHERE BOH_NODOC='…'.UPDATE tbTr_BackOffice_H SET BOH_TGLDOC='…'.INSERT INTO tbTr_BackOffice_D SELECT * FROM tbTr_BoTrans_D WHERE BOD_NODOC='…'.DELETEthe draft header + detail.- If the batch succeeded →
UpdateStock().
UpdateStock()¶
Re-reads the posted detail joined to the item master
(tbTr_BackOffice_D LEFT JOIN tbMaster_Barang ON BOD_PRDCD = PRD_PRDCD) and, per line,
moves inventory through the stock engine (cSTOK.UpdateSaldoAll). Bundle items
(PRD_PAKET = 'Y') are exploded via tbMaster_BarangPaket so each component posts
individually.
Table relationships¶
erDiagram
tbTr_BoTrans_H ||--|{ tbTr_BoTrans_D : "draft header/detail"
tbTr_BackOffice_H ||--|{ tbTr_BackOffice_D : "posted header/detail"
tbTr_BoTrans_H ||--o| tbTr_BackOffice_H : "posts to"
tbMaster_Barang ||--o{ tbTr_BackOffice_D : "item"
tbMaster_Supplier ||--o{ tbTr_BackOffice_H : "supplier (receipts)"
tbTr_BackOffice_H ||--o{ tbMaster_Stock : "moves stock"
tbMaster_NomorDoc ||--o{ tbTr_BackOffice_H : "numbers"
tbTr_BackOffice_H {
string BOH_KODECABANG PK
string BOH_NODOC PK
string BOH_TIPEDOC "doc type 1..9/M/S"
string BOH_KODESUP FK
string BOH_RECORDID "posted status"
string BOH_NOREFF "source doc"
}
tbTr_BackOffice_D {
string BOD_KODECABANG PK
string BOD_NODOC PK
int BOD_NOURUT PK
string BOD_PRDCD FK
double BOD_QTY
double BOD_HRGSAT
string BOD_KODEPBR "P/R for repack"
}
tbMaster_Stock {
string ST_KODECABANG PK
string ST_LOKASI PK
string ST_PRDCD PK
double ST_QTYAKHIR "ending"
double ST_AVGCOST
}
Document types (BOH_TIPEDOC)¶
| Code | Indonesian | Meaning | Stock |
|---|---|---|---|
1 |
Penerimaan Barang (BPB) | Goods receipt from supplier/PO | in |
2 |
Retur Barang | Return to supplier | out |
3 |
Mutasi Stock | Internal transfer between locations | move |
4 |
Barang Hilang | Lost / missing goods | out |
5 |
Mutasi / Konsinyasi Keluar | Transfer / consignment out (packing list, DO) | out |
6 |
Mutasi / Konsinyasi Masuk | Transfer / consignment in | in |
7 |
Stock Opname (& Ganti PLU) | Physical count adjustment / item-code change | ± |
8 |
Pemusnahan Barang | Disposal / write-off | out |
9 |
RePacking | Repackaging (assemble / break packs) | move |
M |
MPP | Inventory adjustment memo | ± |
S |
Pemakaian Sendiri | Own / internal use | out |
5 and 6 are a pair
A transfer creates a 5 (out) at the origin and a 6 (in) at the destination.
Consignment (frmInputKonsinyasi) reuses codes 1, 5, 6, 8, 9. Physical stock count
(7) is entered through its own tbTr_StockOpname_* tables even though its effect is
an adjustment.
What each transaction class does¶
| Class | Posts | Stock effect | Also updates |
|---|---|---|---|
PenerimaanBarang (receipt) |
ProsesBPB |
+ QTYBELI (qty + bonuses); recomputes avg cost |
tbMaster_Barang (PRD_HRGBELI/LASTCOST/AVGCOST/TGLBELI/KODESUP), tbMaster_Stock cost cols; proposed selling price, price history, approval rows; closes the source PO |
ReturBarang (supplier return) |
ProsesReturBarang |
+ QTYRBELI (out) |
avg recompute on cancel |
RePackingBarang |
ProsesRePacking |
'P' component → + QTYKELUAR; 'R' result → + QTYMASUK; recompute result avg |
PRD_AVGCOST of the 'R' item |
MemoPenyesuaianPersediaan (MPP) |
ProsesMPP |
+ QTYMASUK using header BOH_TIPESTOCK; recompute avg when qty > 0 |
PRD_AVGCOST |
BarangHilang (lost / own-use) |
ProsesBarangHilang |
Out via QTYKELUAR; reversals negate and recompute avg |
PRD_AVGCOST on cancel |
PrintNota |
CetakNota |
none — reprints Crystal doc by type | — |
Reversals (Pembatalan…)¶
Every poster has a matching reversal: PembatalanBTB, PembatalanRetur,
PembatalanRePacking, PembatalanMPP, PembatalanNBH, PembatalanPemusnahan,
PembatalanPemakaianSendiri. A reversal re-reads the posted detail, applies the inverse
movement (quantities × −1) through the stock engine, then DELETEs the header + detail from
tbTr_BackOffice_H/_D. Status is tracked on BOH_RECORDID (posted flag) and references via
BOH_NOREFF.
Document numbering¶
Two generators, both in MyLib/HAKFunction.vb (HAKFUNC):
| Generator | Mechanism | When used |
|---|---|---|
GetNomor(ConnStr, KdCab, KdDoc, NmDoc, NoAwal, Digit, Save, …) |
Counter table tbMaster_NomorDoc — reads NomorDoc, +1, zero-pads, optionally persists. |
Most documents. e.g. BP-<branch>-<yyyyMM>-00001. |
NewNumber(ConnStr, Table, Field, Digit, Criteria, Awalan) |
SELECT MAX(field) + 1 — strips the numeric tail, increments, re-pads. |
Where no counter row exists; detail line numbers (MAX(NOURUT)). |
MAX()+1 is a race condition
NewNumber (and inline MAX(NOURUT)+1) can hand the same number to two concurrent
postings, producing duplicate document numbers under load. There is no counter lock on
that path. See Known Issues.
Key columns for posting¶
Header (BOH_): NODOC, TGLDOC, TIPEDOC, KODELOK, TIPESTOCK/TIPESTOCK2,
NOFAKTUR, NOREFF, KODESUP, KODEGUDANG, RECORDID. Detail (BOD_): PRDCD, QTY,
HRGSAT, DISC1/DISC2, BONUS1/BONUS2, PPN, HRGGRAM, KODEPBR, NOURUT.