Anyone can name a T-code. A consultant can read the posting.
In Financial Accounting, every argument ends at the document. Not at the design slide, not at the config screenshot — at the rows SAP actually wrote. So this page does the one thing a static reference cannot: it posts a document in front of you, then opens every table it touched and explains every column.
Pick a business event, set the configuration a consultant actually owns, and press Post. The number range assigns a document number, the database writes fill in one by one, and five tables appear with their real field names. Then click any column header — you get what the field is, where its value came from (user entry, master data, configuration, or derived by SAP), and why it matters when something goes wrong.
This is a teaching model, not a sandbox client. The amounts, accounts and document numbers are illustrative; the mechanics — which table receives which field, which transaction key labels which line, which IMG node determines an account — are standard SAP S/4HANA behaviour. Verify a rate or a field name in the system before you rely on it.
Post a document. Watch the tables fill.
A Fiori-style posting surface. Change the configuration on the left and the document on the right is rebuilt from it — no journal line here is hard-coded. Every column header is a button into the field dictionary.
Configuration & entry
Database tables written
Click any column header above to see what that field holds, where SAP gets it from, and which configuration decides it.
Rendered in SAP Fiori's own semantic palette (brand #0a6ed1,
shell #354a5f, success #107e3e). SAP ships the proprietary
72 typeface; this page substitutes IBM Plex, so treat the look as faithful rather
than pixel-identical.
One posting. Five tables. Each answers a different question.
A new consultant's most common mistake is looking for everything in one table. An FI document is deliberately spread across five, because five different readers need it — the accountant, the auditor, the tax authority, the controller and the subledger.
BKPF
What happened, once. Company code, document number, year, type, dates, currency, rate, and the reference back to the MM or SD document that caused it.
BSEG
What the user posted. The entry view — posting keys, accounts, amounts in document and local currency. The compatibility view in S/4HANA.
ACDOCA
The single source of truth. The Universal Journal: FI + CO + asset + material ledger in one table, after document splitting, with every currency and reporting dimension.
BSET
What the tax authority needs. Base and tax amount per tax code, in every currency — this is what the VAT return reads.
WITH_ITEM
What the payee's certificate needs. Withholding base, withheld amount, code, recipient type and certificate number.
The open- and cleared-item tables you may know from ECC — BSIK/BSAK
(vendor), BSID/BSAD (customer), BSIS/BSAS (G/L) —
still answer queries in S/4HANA, but they are now views over ACDOCA, not separate stores.
The header: the facts that apply to the whole document.
BKPF is small and boring until something is wrong — then it is the first place you look, because almost every "why did it post like that?" question traces back to a header field.
The key is three fields: BUKRS + BELNR + GJAHR —
company code, document number, fiscal year. Document numbers are only unique within a
company code and year, which is why every FI table repeats all three.
What each group of header fields is for
| Group | Fields | Why a consultant reads it |
|---|---|---|
| Identity | BUKRS BELNR GJAHR BLART |
The document type (BLART) drives the number range and which account
types are allowed. A document in the wrong range usually means the wrong type was used. |
| Dates | BLDAT BUDAT MONAT CPUDT |
Document date is the invoice date; posting date decides the period and therefore the tax return. When a VAT report and a G/L balance disagree on timing, this is nearly always why. |
| Currency | WAERS KURSF HWAER |
KURSF is the rate actually used by this document. For a foreign-currency
payment, quoting this field settles the argument about which rate applied. |
| Provenance | AWTYP AWKEY TCODE USNAM |
The reference pair points back to the source: MKPF a material document,
RMRP a logistics invoice, VBRK a billing document. This is
how you prove an FI document came from MM or SD rather than a manual journal. |
| Reversal | STBLG STJAH XREVERSAL |
A filled STBLG means this document was reversed. Reports that forget to
exclude reversals double-count. |
Open any document with FB03 and read the header before the lines. Ninety percent of "wrong posting" tickets are a wrong posting date, a wrong document type, or a wrong exchange rate — all three are header fields.
The entry view: what the user actually typed.
BSEG holds the document as it was entered — before document splitting, one row per line, with the posting keys that decided debit, credit and account type.
Two fields do most of the explaining:
BSCHL
The posting key — what the user chose. It
fixes the account type and the debit/credit sign together:
40/50 G/L debit/credit · 31/21 vendor
credit/debit · 01/11 customer debit/credit ·
70/75 asset · 25 vendor payment ·
89/96 stock / GR-IR.
KTOSL
The transaction key — why SAP generated the
line. This is the single most useful field on the page:
VST input tax · MWS output tax · NVV non-deductible ·
ESA/ESE reverse-charge pair · WIT withholding ·
WRX GR/IR · BSX stock · KDF exchange-rate difference.
SHKZG
The debit/credit indicator, S or
H. Amounts in BSEG are stored unsigned — the sign lives here. Reports
that sum DMBTR without reading SHKZG produce nonsense.
A user picks BSCHL. SAP fills KTOSL. So when a
tax line posts to an unexpected account, the user did not choose that account — the
account key behind the transaction key did, in OB40.
Checking the user's entry is the wrong first move; check the determination.
Amount fields come in pairs: WRBTR in document currency and DMBTR in
local currency, with MWSTS for the tax portion. Clearing adds AUGBL
(the clearing document) and AUGDT — an open item is simply a line where those are empty.
The Universal Journal: one table, every dimension.
S/4HANA's defining change. Where ECC kept FI in BSEG, CO in COEP, assets in ANEP and the material ledger elsewhere — and reconciled them nightly — S/4HANA writes one row carrying all of it.
The consequence is practical: a cost-centre report and a G/L balance can no longer disagree, because they read the same row. The reconciliation job that used to exist has nothing left to do.
The key, and why it has five parts
RLDNR + RBUKRS + GJAHR + BELNR +
DOCLN — ledger, company code, year, document, line. The ledger comes first
because the same posting exists once per ledger. Forget RLDNR = '0L' in a
SE16N query and every amount you see is multiplied by the number of
ledgers.
Field families
| Family | Fields | What it gives you |
|---|---|---|
| Ledger & key | RLDNR RBUKRS GJAHR BELNR DOCLN | Which book, which document, which line. |
| Account | RACCT KTOSL MWSKZ DRCRK | The G/L account plus the reason the line exists and its tax code. |
| Amounts | WSL HSL KSL OSL RWCUR | Transaction, company-code, group and freely-defined currency — signed, unlike BSEG. |
| Reporting | RCNTR PRCTR SEGMENT RFAREA | Cost centre, profit centre, segment, functional area — the dimensions a balance sheet can be sliced by. |
| Subledger | LIFNR KUNNR ANLN1 MATNR | Vendor, customer, asset, material on the same row — no join needed. |
Amounts in ACDOCA are signed; amounts in BSEG are not. That single
difference is behind a surprising number of custom-report defects: logic ported from ECC keeps
reading SHKZG, which ACDOCA does not need, and flips half the lines.
The tax lines: what the VAT return actually reads.
One row per tax code per document. Not per line item — per tax code. Three expense lines sharing one tax code produce one BSET row with the summed base.
The amount fields come in pairs, once per currency, and the naming is worth memorising because every tax report in SAP uses it:
| Currency | Base | Tax | Which currency |
|---|---|---|---|
| Document | FWBAS | FWSTE | The currency on the invoice |
| Local 1 | HWBAS | HWSTE | Company-code currency — what the return is filed in |
| Local 2 | H2BAS | H2STE | Group currency, if configured |
| Local 3 | H3BAS | H3STE | Third currency, often a tax-specific one |
Alongside them: MWSKZ the tax code, KTOSL the account key that sent
it to a G/L account, KSCHL the condition type from the tax procedure,
KBETR the rate as applied, and HKONT the account it landed in.
A non-deductible tax code shows no separate tax line. With account key
NVV the tax is added into the expense or GR/IR line, so BSEG has nothing
tax-shaped in it — but BSET still records the base and the amount. Read BSET, not the lines.
A reverse-charge code produces two BSET rows. The ESE/ESA
pair self-assesses the tax: one row is the input tax you may reclaim, the other the output tax
you owe. The net cash effect is nil, which is exactly why it is easy to miss.
The standard VAT return report is RFUMSV00 (S_ALR_87012357). It reads BSET. If a tax amount is missing from the return but visible in the G/L, the posting reached BSEG without reaching BSET — usually a tax code with no condition record or a manual posting to a tax account.
Withholding tax: a separate engine, a separate table.
VAT and withholding tax are often spoken of together and configured nowhere near each other. VAT is a tax code on a line. Extended Withholding Tax is its own engine, keyed by type and code, writing to its own table.
The key is BUKRS + GJAHR + BELNR + BUZEI +
WITHT — note the last part: one row per withholding type on the line. A
vendor subject to two types produces two rows on the same line.
Type versus code — the distinction that matters
WHT type · T059P
When and how tax is calculated: at invoice or at payment, the base amount type, rounding, accumulation, and whether the posting is with or without offsetting. Changing a type changes timing.
WHT code · T059Z
How much: the rate (QSATZ) and the
income type (QEKAR) that decides which return the amount appears on. Changing a
code changes the number.
Vendor data · LFBW
Per company code: which types apply, the liable flag, the default code and the recipient type. If the liable flag is off, nothing is withheld — and that is the first thing to check when a deduction is missing.
For a deduct-at-payment type, the invoice writes a WITH_ITEM row with the type, the code and the base — but amount zero. The amount only appears on the payment document. A row with a base and no amount is not a defect; it is the invoice half of a two-document story. Run the payment in the simulator above and watch the amount appear on the second document.
Amount fields follow the same per-currency pattern as BSET: WT_QSSHB/WT_QBSHB
base and tax in document currency, WT_QSSHH/WT_QBSHH in local currency 1,
then …SH2 / …SH3 for further currencies. QSREC carries the
recipient type and CTNUMBER the certificate number.
Reporting runs through RFIDYYWT — Generic Withholding Tax Reporting (S_P00_07000134). One selection screen produces forms, lists and e-filing files; which of them you get is decided by the output group, not by the selection. A report that "runs but produces nothing" is usually the wrong output group.
Document splitting: the same document, told twice.
Turn on splitting in the workbench and the row count changes. Nothing was posted twice — you are looking at the second view of one document.
A user enters an expense against a profit centre and a vendor line with none; the vendor is a balance-sheet account and carries no reporting dimension of its own. Document splitting pushes the characteristics of the P&L lines onto the balance-sheet lines, in proportion to the amounts, so that a balance sheet can be produced per profit centre or segment.
Entry view — BSEG
What the user posted. The vendor and tax lines are single rows with no profit centre. This is what FB60 showed them.
GL view — ACDOCA
After splitting. The vendor and tax lines are now several rows, one per profit centre, each balanced in its own right.
Why it matters
A report that reads "the" profit centre of a document must decide which line's profit centre it means. Often there is no single answer.
Tax lines multiply. One invoice across two profit centres produces two tax lines in the GL view, each with its own dimension. A tax report joining on "the tax line" of a document will find two and must decide whether to sum them or pick one.
Zero-amount split lines exist. A characteristic can be carried with
0.00. Reports that do not skip them show phantom rows — and a reviewer who sees
a 0.00 line assumes the report is broken.
In FB03, always choose the General Ledger View and name the ledger before discussing segment or profit centre. Arguing about dimensions from the entry view is arguing about a document that does not exist in the ledger.
Every line carries several amounts — and all of them must balance.
Switch the workbench to USD and watch a second and third amount column come alive. S/4HANA stores up to ten currencies per line; most implementations use three.
| Field | Currency type | What it is |
|---|---|---|
WSL | — | Transaction currency — what the invoice was written in. |
HSL | 10 | Company-code currency (local 1) — what the statutory books and the tax return are in. |
KSL | 30 | Group currency — what consolidation reads. |
OSL / VSL | 40+ | Freely defined — a hard currency, an index currency, or a tax-specific currency. |
Each amount is converted at the moment of posting using a rate from TCURR,
selected by rate type (KURST, commonly M for average) and valid-from
date (GDATU, which is stored inverted — a detail that has broken many a
custom report). Maintain rates in OB08.
WSL = 0, ΣHSL = 0, ΣKSL = 0. If the
transaction currency balances but the local currency does not, you are not looking at a posting
error — you are looking at a rounding or rate problem.
Rates are set at posting and then frozen. Period-end foreign-currency valuation (FAGL_FCV) does not change them — it posts additional lines for the difference on open items. So an open item legitimately shows an old rate; the revaluation sits beside it, not on it.
Nothing on a line is typed. Trace any field to the node that decides it.
This is the table a consultant actually works from. Read it right to left: a field on the document, the thing that determines it, and the IMG node or transaction where you change it.
T030K). This — not the user — decides where a tax line lands.BSX picks the stock account from the material's valuation class, WRX the GR/IR clearing account.ERL sends billing to the right revenue line while output tax goes to its own account.QSATZ) and the income type that decides which statutory return the amount lands on.TCURR by rate type and valid-from date. The document freezes the rate it found at posting.Two of these are worth internalising because they are asked at interview: a tax code belongs to a country, not a company code (OBBG), and the account key, not the user, chooses the tax account (OB40).
How to read any FI document in sixty seconds.
The same order, every time. It works because it moves from the facts that apply to the whole document down to the line that is actually wrong.
Header first, in the general ledger view
FB03 → General Ledger View, and name the ledger. Read
BLART, BUDAT, WAERS, KURSF. Most
"wrong posting" reports die here.
Check it balances, per currency
ΣHSL = 0 and ΣWSL = 0. If only one of them balances, the
problem is a rate or a rounding difference, not the posting logic.
Read KTOSL down the lines
It tells you which lines SAP generated and why. A missing VST line means
tax determination did not fire; an unexpected KDF means a currency difference.
Reconcile tax against its own table
VAT in BSET, withholding in WITH_ITEM. Compare the base, not just the tax — a wrong base with a right rate is a different defect from a wrong rate.
Three-layer diagnosis, in order
Does the master data exist (vendor liable flag, tax classification)? Is the configuration right for this company code? And what was actually posted? Checking them out of order wastes the most time.
Isolate with a discriminating pair
Find two documents that differ in exactly one variable — one currency, one tax code, one profit centre. The difference between them is the cause. One document is an anecdote; a pair is an argument.
Check the design before raising anything
Never raise as a defect what the signed design says is intended. Evidence before action: table trace, document, configuration — then raise it.
The module around these documents — organisational units, master data, ledgers and currencies, the period-end close, and Thailand's VAT and withholding returns — is on the companion page: Functional Module of Finance — SAP FI. How configuration turns into a posting in the first place is on the FI/CO Configuration Simulator, and the consulting role around it all is in Scope of Work.