Standard Cost Marking and Release Status — Reading It Straight from KEKO, MBEW and MBEWH
If you run a standard cost estimate every month, sooner or later someone asks a question no single SAP screen answers well: which materials were only marked, which were actually released, and when? Opening CK11N or CK24 material by material is not an answer when there are hundreds of them.
Three tables carry the whole story — KEKO, MBEW and MBEWH. Below are the queries I use to read status, monthly trend and missed releases straight out of them, in ABAP SQL and in native HANA SQL for the DB02 SQL editor.
The three states of a cost estimate
Estimating, marking and releasing are three separate acts, and each leaves its trace in a different field. Getting that mapping right is most of the job:
| Status | How it is recognised | What it means |
|---|---|---|
| ESTIMATED_ONLY | FREIG ≠ 'X', VORMDAT = 00000000 | The estimate was run (CK11N/CK40N) and nothing else happened. The material master still carries the old price. |
| MARKED_NOT_RELEASED | FREIG ≠ 'X', VORMDAT > 00000000 | Marked (CK24 marking): MBEW-ZKPRS / ZKDAT carry the future price, but the current standard price is untouched. |
| RELEASED | FREIG = 'X', FREIDAT filled | Released (CK24 release): MBEW-STPRS carries the new standard price and MBEW-LAEPR is the release date. |
Tables and fields
| Field | Description | Note |
|---|---|---|
| KEKO | Cost estimate header. One row per material × costing date (KADKY). | Filter with KALKA='01' (standard cost estimate), TVERS='01' (valuation version), LOEKZ = blank (not flagged for deletion). |
| KEKO-FREIG | Release of Standard Cost Estimate. 'X' once released. | — |
| KEKO-FREIDAT / FREIUSR | Date on Which Cost Estimate Released in Material Master / the user who released it. | This is the field that carries the release date. It is not KADKY, the costing date. |
| KEKO-VORMDAT / VORMUSR | Date on Which Cost Estimate Was Marked / the user who marked it. | Marking is decided on this field. |
| KEKO-KKZMA | Costs Entered Manually in Additive or Automatic Cost Est. | The name invites the assumption that this is the marking indicator. It is not — it flags manually entered additive costs. Do not use it to decide marking. |
| MBEW-STPRS / PEINH | Current standard price / price unit. This is where a release lands. | — |
| MBEW-ZKPRS / ZKDAT | Future Price / Valid from. Marking writes the future standard price here. | — |
| MBEW-LAEPR | Date of Last Price Change. Updated by the release. | Comparing it with KEKO-FREIDAT tells you whether the release actually reached the material master. |
| MBEW-VMSTP / VJSTP | Previous month / previous year standard price. | — |
| MBEWH | Material valuation history. A per-period snapshot (LFGJA/LFMON), which is where the monthly standard price trend comes from. | The current period has not rolled into MBEWH yet, so the last row has to be topped up from MBEW. |
Q1 · Latest estimate per material, with current status (ABAP SQL)
One row per material: released or not, release date, who released it, marking date, and the standard price sitting on the material master right now. The subquery on MAX(KADKY) is what keeps only the latest run. The RELEASE_CONSISTENCY column compares FREIDAT against LAEPR, so a release that never reached the material master shows up as CHECK_LAEPR.
SELECT
k~matnr, k~werks, t~maktx AS material_text,
k~kadky AS costing_date, k~kadat AS valid_from, k~bidat AS valid_to,
k~kalst AS costing_level, k~losgr, k~meins, k~kalnr,
"--- release / marking ---
k~freig AS released_flag,
k~freidat AS released_on,
k~freiusr AS released_by,
k~vormdat AS marked_on,
k~vormusr AS marked_by,
CASE WHEN k~freig = 'X' THEN 'RELEASED'
WHEN k~vormdat > '00000000' THEN 'MARKED_NOT_RELEASED'
ELSE 'ESTIMATED_ONLY'
END AS cost_status,
"--- price currently on the material master ---
m~vprsv AS price_control, m~stprs AS std_price_now, m~peinh AS price_unit,
m~zkprs AS future_price, m~zkdat AS future_valid_from,
m~laepr AS last_price_change,
m~vmstp AS std_price_prev_month, m~vjstp AS std_price_prev_year,
"--- release date vs last price change ---
CASE WHEN k~freig = 'X' AND k~freidat = m~laepr THEN 'OK'
WHEN k~freig = 'X' THEN 'CHECK_LAEPR'
ELSE ''
END AS release_consistency
FROM keko AS k
LEFT OUTER JOIN mbew AS m
ON m~matnr = k~matnr AND m~bwkey = k~bwkey AND m~bwtar = k~bwtar
LEFT OUTER JOIN makt AS t
ON t~matnr = k~matnr AND t~spras = @sy-langu
WHERE k~kalka = '01' AND k~tvers = '01'
AND k~werks = @p_werks
AND k~loekz = @space
AND k~kadky = ( SELECT MAX( k2~kadky ) FROM keko AS k2
WHERE k2~matnr = k~matnr AND k2~werks = k~werks
AND k2~bwkey = k~bwkey AND k2~bwtar = k~bwtar
AND k2~kalka = k~kalka AND k2~tvers = k~tvers
AND k2~loekz = @space )
ORDER BY k~werks, k~matnr
INTO TABLE @DATA(lt_current_status).
Q2 · Monthly trend of cost estimates (ABAP SQL)
The same source without the “latest only” restriction, so every monthly run stays in the result. substring(KADKY,1,6) gives YYYYMM for grouping, and dats_days_between measures how long each estimate sat between marking and release.
SELECT
k~matnr, k~werks, t~maktx AS material_text,
k~kadky AS costing_date,
substring( k~kadky, 1, 6 ) AS costing_yyyymm,
k~kadat AS valid_from, k~bidat AS valid_to,
k~kalst AS costing_level, k~losgr, k~meins, k~kalnr,
k~cpudt AS created_on, k~erfnm AS created_by,
k~freig AS released_flag,
k~freidat AS released_on,
k~freiusr AS released_by,
k~vormdat AS marked_on,
k~vormusr AS marked_by,
CASE WHEN k~freig = 'X' THEN 'RELEASED'
WHEN k~vormdat > '00000000' THEN 'MARKED_NOT_RELEASED'
ELSE 'ESTIMATED_ONLY'
END AS cost_status,
"days from marking to release
CASE WHEN k~freig = 'X' AND k~vormdat > '00000000'
THEN dats_days_between( k~vormdat, k~freidat )
ELSE 0
END AS days_mark_to_release
FROM keko AS k
LEFT OUTER JOIN makt AS t
ON t~matnr = k~matnr AND t~spras = @sy-langu
WHERE k~kalka = '01' AND k~tvers = '01'
AND k~werks = @p_werks
AND k~matnr = @p_matnr "remove this line to read every material
AND k~loekz = @space
AND k~kadky BETWEEN @p_from AND @p_to
ORDER BY k~werks, k~matnr, k~kadky
INTO TABLE @DATA(lt_monthly_trend).
Q3 · Standard price history (ABAP SQL)
Q2 tells you when the estimate was run. Q3 tells you what the material master actually carried, period by period. MBEWH is written at period close, so the current period is missing — the second SELECT tops up the last row from MBEW.
SELECT
h~matnr, h~bwkey AS valuation_area, h~bwtar AS valuation_type,
h~lfgja AS fiscal_year, h~lfmon AS period,
h~vprsv AS price_control,
h~stprs AS std_price, h~verpr AS moving_avg_price, h~peinh AS price_unit,
h~lbkum AS stock_qty, h~salk3 AS stock_value, h~bklas AS valuation_class
FROM mbewh AS h
WHERE h~matnr = @p_matnr
AND h~bwkey = @p_werks
ORDER BY h~matnr, h~bwkey, h~lfgja, h~lfmon
INTO TABLE @DATA(lt_price_history).
" current period - not in MBEWH yet
SELECT SINGLE
matnr, bwkey, bwtar, lfgja, lfmon,
vprsv, stprs, verpr, peinh, lbkum, salk3, bklas,
zkprs, zkdat, laepr, vmstp, vjstp
FROM mbew
WHERE matnr = @p_matnr
AND bwkey = @p_werks
INTO @DATA(ls_price_now).
Q4 · Finding the releases that were missed
Marked but never released. This is the month-end query: it lists exactly the materials whose CK24 release was skipped. Putting ZKPRS (future price) next to STPRS (current price) also shows what each price will become once the release runs.
SELECT
k~matnr, k~werks, t~maktx AS material_text,
k~kadky AS costing_date,
k~vormdat AS marked_on,
k~vormusr AS marked_by,
m~stprs AS std_price_now,
m~zkprs AS future_price,
m~zkdat AS future_valid_from,
m~laepr AS last_price_change
FROM keko AS k
LEFT OUTER JOIN mbew AS m
ON m~matnr = k~matnr AND m~bwkey = k~bwkey AND m~bwtar = k~bwtar
LEFT OUTER JOIN makt AS t
ON t~matnr = k~matnr AND t~spras = @sy-langu
WHERE k~kalka = '01'
AND k~tvers = '01'
AND k~werks = @p_werks
AND k~loekz = @space
AND k~kadky BETWEEN @p_from AND @p_to
AND k~freig <> 'X' "not released
AND k~vormdat > '00000000' "but marked
ORDER BY k~werks, k~matnr, k~kadky
INTO TABLE @DATA(lt_not_released).
Running the same thing in DB02 (native HANA SQL)
To paste these into DB02 → DBA Cockpit → Diagnostics → SQL Editor, four things have to change:
- Add the MANDT condition yourself — there is no automatic client handling.
- Join notation
~becomes. - Host variables such as
@p_werksbecome literals. - Drop
INTO TABLE @DATA(...). Functions change too:dats_days_between→DAYS_BETWEEN(TO_DATE(...)).
▷ Q1 in native HANA SQL — edit the plant and client values directly in the WHERE clause.
SELECT
K.MATNR, K.WERKS, T.MAKTX AS MATERIAL_TEXT,
K.KADKY AS COSTING_DATE, K.KADAT AS VALID_FROM, K.BIDAT AS VALID_TO,
K.KALST AS COSTING_LEVEL, K.LOSGR, K.MEINS, K.KALNR,
K.FREIG AS RELEASED_FLAG,
K.FREIDAT AS RELEASED_ON,
K.FREIUSR AS RELEASED_BY,
K.VORMDAT AS MARKED_ON,
K.VORMUSR AS MARKED_BY,
CASE WHEN K.FREIG = 'X' THEN 'RELEASED'
WHEN K.VORMDAT > '00000000' THEN 'MARKED_NOT_RELEASED'
ELSE 'ESTIMATED_ONLY'
END AS COST_STATUS,
M.VPRSV AS PRICE_CONTROL, M.STPRS AS STD_PRICE_NOW, M.PEINH AS PRICE_UNIT,
M.ZKPRS AS FUTURE_PRICE, M.ZKDAT AS FUTURE_VALID_FROM,
M.LAEPR AS LAST_PRICE_CHANGE,
M.VMSTP AS STD_PRICE_PREV_MONTH, M.VJSTP AS STD_PRICE_PREV_YEAR,
CASE WHEN K.FREIG = 'X' AND K.FREIDAT = M.LAEPR THEN 'OK'
WHEN K.FREIG = 'X' THEN 'CHECK_LAEPR'
ELSE ''
END AS RELEASE_CONSISTENCY
FROM KEKO AS K
LEFT OUTER JOIN MBEW AS M
ON M.MANDT = K.MANDT AND M.MATNR = K.MATNR
AND M.BWKEY = K.BWKEY AND M.BWTAR = K.BWTAR
LEFT OUTER JOIN MAKT AS T
ON T.MANDT = K.MANDT AND T.MATNR = K.MATNR AND T.SPRAS = 'E'
WHERE K.MANDT = '100'
AND K.KALKA = '01'
AND K.TVERS = '01'
AND K.WERKS = '1010'
AND K.LOEKZ = ''
AND K.KADKY = ( SELECT MAX( K2.KADKY ) FROM KEKO AS K2
WHERE K2.MANDT = K.MANDT AND K2.MATNR = K.MATNR
AND K2.WERKS = K.WERKS AND K2.BWKEY = K.BWKEY
AND K2.BWTAR = K.BWTAR AND K2.KALKA = K.KALKA
AND K2.TVERS = K.TVERS AND K2.LOEKZ = '' )
ORDER BY K.WERKS, K.MATNR;
▷ Q5 — monthly progress count — total, released, marked-only and estimated-only per plant and costing month. This is the one to watch during close.
SELECT
K.WERKS,
SUBSTRING( K.KADKY, 1, 6 ) AS COSTING_YYYYMM,
COUNT(*) AS TOTAL_CNT,
SUM( CASE WHEN K.FREIG = 'X' THEN 1 ELSE 0 END ) AS RELEASED_CNT,
SUM( CASE WHEN K.FREIG <> 'X' AND K.VORMDAT > '00000000'
THEN 1 ELSE 0 END ) AS MARKED_ONLY_CNT,
SUM( CASE WHEN K.FREIG <> 'X' AND K.VORMDAT = '00000000'
THEN 1 ELSE 0 END ) AS ESTIMATED_ONLY_CNT,
MIN( CASE WHEN K.FREIG = 'X' THEN K.FREIDAT END ) AS FIRST_RELEASE_DATE,
MAX( CASE WHEN K.FREIG = 'X' THEN K.FREIDAT END ) AS LAST_RELEASE_DATE
FROM KEKO AS K
WHERE K.MANDT = '100'
AND K.KALKA = '01'
AND K.TVERS = '01'
AND K.WERKS = '1010'
AND K.LOEKZ = ''
AND K.KADKY BETWEEN '20250101' AND '20261231'
GROUP BY K.WERKS, SUBSTRING( K.KADKY, 1, 6 )
ORDER BY K.WERKS, COSTING_YYYYMM;
Which number is the released amount
The released amount is MBEW-STPRS / MBEWH-STPRS. An estimate that has not been released has no price on the material master, so its value has to come from KEPH (cost components KST001–KST120) or CKIS (itemization, sum of WERTN) — and those two do not agree with each other, because overhead is treated differently in each.
KEPH also holds four to eight variant rows for the same KALNR / KADKY, keyed by the KEART / KKZST / KKZMA combination. Decide which rows you are summing before you sum them.
▷ CKIS itemization total — the sum is on the costing lot size (KEKO-LOSGR), so divide by LOSGR for a unit price.
SELECT SUM( wertn ) FROM ckis WHERE kalnr = @lv_kalnr AND kalka = '01' AND kadky = @lv_kadky AND tvers = '01' AND bwvar = @lv_bwvar INTO @DATA(lv_total).
Tables: KEKO · MBEW · MBEWH · CKIS · KEPH · MAKT | Transactions: CK11N · CK24 · CK40N · DB02
Comments
Post a Comment