Joining BKPF and BSEG without losing your mind
The four key fields that link an SAP accounting document header to its line items — and why order matters.
In SAP FI, an accounting document is split across two tables: BKPF holds the header (one row per document) and BSEG holds the line items (many rows per document). Get the join keys wrong and you either duplicate rows or silently drop postings from other company codes.
The four keys
A document is only unique across all four of these together:
MANDT— clientBUKRS— company codeBELNR— document numberGJAHR— fiscal year
Leaving out BUKRS or GJAHR is the classic mistake: BELNR is only unique within a company code and fiscal year, so a two-field join fans out across every company code that happens to reuse the same document number.
SELECT
h.bukrs,
h.belnr,
h.gjahr,
h.blart, -- document type
h.budat, -- posting date
l.buzei, -- line item number
l.hkont, -- G/L account
l.shkzg, -- debit/credit indicator (S/H)
l.dmbtr -- amount in local currency
FROM bkpf AS h
JOIN bseg AS l
ON l.mandt = h.mandt
AND l.bukrs = h.bukrs
AND l.belnr = h.belnr
AND l.gjahr = h.gjahr
WHERE h.bukrs = '1000'
AND h.gjahr = '2026'
ORDER BY h.belnr, l.buzei;
Amounts are unsigned — read SHKZG
DMBTR is always stored as a positive number. The debit/credit direction lives in SHKZG (S = debit, H = credit). If you sum DMBTR directly you get gross turnover, not a net balance.
SELECT
l.hkont,
SUM(CASE WHEN l.shkzg = 'H' THEN -l.dmbtr ELSE l.dmbtr END) AS net_local
FROM bkpf AS h
JOIN bseg AS l
ON l.mandt = h.mandt
AND l.bukrs = h.bukrs
AND l.belnr = h.belnr
AND l.gjahr = h.gjahr
WHERE h.bukrs = '1000'
AND h.gjahr = '2026'
GROUP BY l.hkont;
When not to use it
For reporting, don't reconstruct balances from BKPF/BSEG line by line — that's what ACDOCA (S/4) or the summary tables (GLT0, FAGLFLEXT in classic) are for. Reach for the header/line join when you need document-level detail: audit trails, drill-downs, or reconciling a specific posting.