MM Customisation Guide (is it possible — and where is the hook?)
This is a page for a functional MM consultant, not for a developer. Your job is never to write the ABAP. Your job is to answer three questions with confidence:
- Can standard SAP already do this? (most of the time, yes — and you save the client money)
- If not, is there an official place to hook custom logic in? (and does it actually work on this system?)
- Should we? (every line of custom code is a permanent cost at every upgrade)
The scenario pages stay free of developer detail on purpose. When a scenario reaches the end of what configuration can do, it says so and sends you here — to the exact section for that topic.
☁️ Working on SAP Cloud, not on-premise? Start with “Which SAP are you on?” — the tools are completely different, and almost every transaction on this page is unavailable in Public Cloud.
🌍 First — which SAP are you on? The toolbox is completely different
This matters more than anything else on the page. The business problem is identical in every SAP. The tools you are allowed to touch are not. Someone who learned customisation on an on-premise system and then walks into a Public Cloud project will find that almost every transaction they know has been taken away.
🏢 On-Premise
The system runs on hardware you (or your hosting partner) control. You can do anything — full SPRO, full ABAP, your own tables and programs.
Our practice IDES is this one.
🏗️ Cloud, Private Edition
Your own single-tenant system, but hosted and upgraded by SAP. Technically almost the same freedom as on-premise — but SAP pushes you hard to stop using it.
The usual home for a company moving off ECC.
☁️ Cloud, Public Edition
A shared, standardised system SAP upgrades every quarter, for everybody. You get a fixed set of Fiori apps. There is no SPRO and no SE38.
You may only use what SAP has officially released.
Old SAP projects changed SAP's own programs. Then every upgrade broke, cost a fortune to re-test, and companies stopped upgrading. Clean core is SAP's answer: never touch the standard — only plug into the sockets SAP officially provides.
Everyday picture: renting a flat. You may hang a picture on the hooks the landlord left in the wall. You may not knock a hole through the wall — even if the hole would look better — because the landlord repaints every three months and the wall must still be a wall.
Same job, different tool — the translation table
| What you want to do | 🏢 On-Premise & 🏗️ Private Cloud (what this page teaches) | ☁️ Public Cloud |
|---|---|---|
| Change a setting | SPRO — the whole IMG tree, thousands of activities | ❌ no SPRO. You get a fixed list of self-service configuration screens (SSCUIs), opened from the Manage Your Solution app → Configure Your Solution (newer tenants use SAP Central Business Configuration instead). If SAP did not publish an SSCUI for it, you cannot change it. |
| Look inside a table | SE16 / SE16N — any table | ❌ not available. You use released CDS views and analytical apps instead. |
| Add a field to a screen | Append structure + screen exit + ABAP | ✅ Custom Fields and Logic app — a key user adds the field, and SAP extends the table, the CDS view and the OData service for you. Usually no code at all. |
| Find a BAdI | SE18, or SE16 → SXS_ATTRT | ✅ the Custom Logic app lists the released BAdIs (SAP calls each one an Extension Point). The catalogue is much smaller — only what SAP guarantees to keep. |
| Write the BAdI code | SE19 + full ABAP in SE80/ADT | ✅ a small ABAP web editor inside the Custom Logic app. ⚠️ Restricted ABAP only — you may call released APIs and nothing else. No SELECT straight from a table. |
| Classic user exit (SMOD/CMOD) | ✅ available (older, needs an access key) | ❌ gone completely. There is no replacement — if no released BAdI exists, the answer is genuinely "no". |
| Write a full program / report | SE38, any ABAP | ⚠️ only through Developer Extensibility — ABAP Cloud written in Eclipse/ADT, restricted language, and only if your project has a 3-system landscape. Otherwise it must live outside, on SAP BTP. |
| Who is allowed to do it | Developer with S_DEVELOP + access key | A key user (often the functional consultant!) for tiers 1; a developer for tier 2; a BTP team for tier 3. |
The three tiers of cloud extensibility — in plain words
| Tier | Name | What you can do | Where you work | Who |
|---|---|---|---|---|
| 1 | Key user (in-app) MM can do this | Add custom fields · write small checks and calculations · implement a released BAdI · build simple custom business objects, CDS views and analytical queries · change the screen layout | Fiori apps inside the system: Custom Fields and Logic, Custom Logic, Custom Business Objects, UI adaptation | ⭐ A key user — very often the functional consultant. This tier is the reason MM people should read this page. |
| 2 | Developer (on-stack) ABAP | Real ABAP applications, but written in the restricted ABAP Cloud language, using only released APIs. Also called "embedded Steampunk". | Eclipse with ABAP Development Tools (ADT), connected to the system | A developer. ⚠️ On Public Edition this is only available with a 3-system landscape. |
| 3 | Side-by-side BTP team | A separate application that talks to SAP through released APIs and events. Any language — ABAP, Java, Node.js. | SAP BTP (Business Technology Platform) — outside the SAP system | A BTP / integration team. Used when the logic is big, or must survive independently of SAP's upgrades. |
"If SAP has not released it, it cannot be done — and there is no workaround."
On-premise, "no" almost always really means "yes, but it will cost you". On Public Cloud, "no" can mean no. So the evaluation below gains one extra question at the front: is there a released extension point for this at all? If there is not, the honest answers are: change the business process, wait for SAP to release it (you can request it), or build it side-by-side on BTP.
Our IDES is an on-premise system, so everything from Step 1 downwards on this page is the on-premise / private-cloud toolbox — SE18, SMOD, SE16 and the rest, and you can practise all of it safely by looking.
⭐ But the thinking transfers 100%, and the thinking is what gets you hired:
- Always try configuration first — true in both worlds, and even more true in the cloud.
- Look for an official socket before considering anything else — on-premise it is a BAdI; on Public Cloud it is a released Extension Point. Same idea, different door.
- Check the hook is really usable before promising a date — on-premise the plumbing may be missing; on the cloud the object may simply not be released.
- Ask "should we?" — this question never changes.
💡 In interviews this is a favourite question. The clean answer is: "On-premise I search SXS_ATTRT for a BAdI and fall back to a classic user exit. On Public Cloud there are no classic exits, so I check the Custom Logic app for a released Extension Point, and if there is none the honest answer is a process change or a side-by-side extension on BTP. Both start the same way — prove configuration cannot do it first."
🔍 Step 1 — How to check whether something can be customised
🏢🏗️ The tools below are the on-premise / private-cloud toolbox — which is what our practice IDES is. On ☁️ Public Cloud the order of the questions is the same, but the tools are the Fiori ones from the table above.
Somebody asks for a change. Before you answer, run these six checks in this order. Stop at the first one that works — that is the cheapest answer, and the cheapest answer is almost always the right one.
| # | Check | How you check it — the exact click | If yes… |
|---|---|---|---|
| 1 | Is it already a setting? MM | SPRO → the IMG tree → use Edit → Find and type a keyword. Or search the transaction list: SE16 → TSTCT → TTEXT = *your keyword*, language E. |
✅ Done — no code, no risk, no upgrade cost. |
| 2 | Is it just field selection? MM | A surprising number of "we need a change" requests are really "make this field mandatory / hidden / display-only". Material master: OMS9. Purchasing documents: OLME → Field Selection. Movement types: OMJJ. | ✅ Done — still configuration. |
| 3 | Is there a BAdI? MM finds it | ⭐ The most useful search you will ever learn: SE16 → SXS_ATTRT → SPRSL = E, EXIT_NAME = a pattern such as ME_* (purchasing), MB* (goods movements), MRM* (invoices), *MATERIAL*, *BATCH*. You get the name and a plain-English description.Then SE18 → type the name → Display to read what it offers. |
✅ Best case. Modern, no access key, survives upgrades. |
| 4 | Is there a classic user exit? MM finds it | SE16 → MODSAPT → SPRSL = E, NAME = MM06E*, MB*, M06B*, MG*… → you get the enhancement name and its description.Then SMOD → type the enhancement → Display → Components to see its function exits, screen exits and menu exits. |
⚠️ Works, but older. Needs CMOD and often an access key. |
| 5 | Is the hook actually USABLE here? BASIS/ABAP | ⭐⭐ The check everybody skips. "The exit exists" ≠ "the exit is ready". Confirm the function module really exists: SE16 → TFDIR → FUNCNAME = the exit name. Then ask a developer to confirm the customer function group behind it has been generated. |
See the warning box below — this is how promises get broken. |
| 6 | Should we? MM | Ask: how much of the need does the config answer already cover? Is the rest cosmetic? Who tests this at every upgrade for the next ten years? | The judgement that separates a senior consultant from a junior one. |
We needed a batch number with a letter prefix. The exit EXIT_SAPLV01Z_002 exists in TFDIR. The enhancement SAPLV01Z exists in MODSAP. A CMOD project ZZBATCH exists and is active. Everything looked ready.
It was not. Creating the code container failed with message DS027 — "Program names ZX... are reserved for includes of exit function groups" — because the customer function group behind the exit had never been generated on this system. Fixing that is a developer job (SE80), and often needs an SSCR access key from SAP.
💡 The lesson to carry into every project: confirm the hook is usable before you promise a date. Four green lights and one missing piece still means "no".
🪜 Step 2 — The five layers, cheapest first
Every requirement lands on one of these five rungs. Always start at the top.
| Rung | Layer | Code? | What it is — in plain words |
|---|---|---|---|
| 1 | Configuration MM | ❌ none | Settings in SPRO — number ranges, movement types, tolerance limits, search strategies, account determination. Most requirements end here. No upgrade cost, ever. |
| 2 | Master data MM | ❌ none | Ticks and values on the material, vendor, info record, batch, class, characteristic. Often mistaken for a system change when it is really just data. |
| 3 | BAdI MM scopes ABAP writes | ✅ small | A socket SAP deliberately left in its own program. No access key, SAP’s code untouched, survives upgrades. Always look for one of these before rung 4. |
| 4 | Classic user exit MM scopes ABAP writes | ✅ yes | The older mechanism: an enhancement switched on by a project, with your code in a Z… container. Works, but needs more plumbing — and sometimes an access key. |
| 5 | Custom objects / modification ABAP | ✅ yes | Your own tables and programs — or, worst case, changing SAP’s own code. A permanent maintenance cost. Almost never the right answer in MM. |
🧰 Step 3 — What must be in place before any code is written
You do not need these to search and evaluate — searching is safe and is the functional consultant's job. You need them the moment somebody actually builds something.
| What | Why it is needed | Who arranges it | What breaks without it |
|---|---|---|---|
| A development system — never production | An active enhancement runs immediately for everyone. | BASIS | A bug stops live business. |
Developer authorisation (S_DEVELOP) | To open SE18/SE19/SE38 in change mode. | BASIS | You can display but not create. |
| Developer access key (SSCR) | SAP asks for a one-time key before you touch objects in SAP's own namespace — which classic user exits are. | BASIS requests it from SAP | You are stopped at the last step, after everyone thinks it is done. |
| A package + transport request | To carry the change from DEV → QA → Production. | BASIS | Choosing $TMP (Local Object) means the work can never leave this system. Fine for learning; fatal for a project. |
Customer namespace — names start with Z or Y | SAP guarantees it will never ship an object with that name. | MM agrees the naming standard | An upgrade overwrites your work. |
| A written functional spec | Says exactly when the logic fires, what data it receives, what it must return, and what must NOT change. | ⭐ MM — this is your deliverable | The developer guesses, and the guess is wrong. |
| A test plan, including the negative case | Prove it fires when it should — and, more importantly, that everything else still works. | MM | You discover on go-live that goods receipts no longer post. |
- The hook: "BAdI
ME_PROCESS_PO_CUST, methodCHECK." - What it receives: "the whole purchase order header and items."
- What it must do: "raise an error message and stop the save when <the rule> is broken."
- What it must NOT touch: "any document that is not document type
NBin company codePSPK."
That last line is the one juniors forget, and it is the one that prevents a system-wide outage. An enhancement should change as little as possible.
📚 Step 4 — The catalogue: what MM offers on this system
Every name below was read live from our IDES (DEV / client 250) using the two searches taught above — SXS_ATTRT for BAdIs and MODSAPT for classic user exits. These are not textbook lists; they are what this system really has. Jump to your topic:
🔢 Batch management
Batch numbers, and batch data filled automatically at goods movement
🛒 Purchase orders
Checks before save, your own fields, your own tab, pricing
📝 Purchase requisitions
Checks, own data, source determination, re-approval
📦 Goods movements / MIGO
Document checks, your own MIGO tab, reservations
🧾 Invoice verification
Header checks, tolerances, payment terms
🏷️ Material master
Field checks, defaults, number assignment
✅ Release strategy
Who approves, and on what value
🖨️ Output & forms
Where labels and printouts are really changed
🔢 Batch management — the hooks that exist, and the one that does not
Referenced from Scenario 16, Part C.
SXS_ATTRT, pattern *BATCH* → 12 results) and none of them fits. Most of them are not even about material batches — CM_BADI_BATCH_JOBS, PLM_AUDIT_BATCH_JOBS and friends are about background jobs. ⚠️ A classic naming trap: always read the description, never trust the name.
| Requirement | Hook (verified live) | Type | Notes |
|---|---|---|---|
⭐ Batch number must carry a text prefix (e.g. RMLL000001) |
EXIT_SAPLV01Z_002 — assign internal batch number( _001 = check an externally typed batch number) |
user exit enhancement SAPLV01Z |
SAP receives material and plant, and expects the finished batch number back. Switch it on with OMJJ → Create New Batch = B ("Automatic/manual and check in USER EXIT"). ⛔ Blocked on our system — see the DS027 warning above. Try configuration first: numeric bands per material type do 90% of this with zero risk (Scenario 16 · B3). |
| Propose a batch number on the goods-movement screen | MBCFC010 — Propose batch number on Inventory Management item screen |
user exit | A softer alternative to the one above: it suggests a number in the screen field instead of generating it deep in the posting logic. Often enough. |
| Maintain batch master data automatically during a goods movement | MBCFC003 — Maintenance of batch master data for goods movements |
user exit | e.g. fill the supplier, country of origin or production date on the batch automatically at goods receipt. |
| Fill batch characteristics automatically | MBCFC004 — Maintenance of batch specifications for goods movements |
user exit | Writes classification values without anyone typing them in MSC2N. |
| Extra checks on the batch master screens | BATCH_MASTER |
BAdI ✅ | Fires in MSC1N/MSC2N. ⚠️ Not for number generation — a common confusion. |
| Classify a batch before the goods receipt posts | MILL_BEFORE_BATCH_CL |
BAdI ✅ | Mill Products add-on; classification only. |
↩️ Back to Scenario 16 — Batch Management.
🛒 Purchase orders — the richest set of hooks in MM
Our system has 50 BAdIs whose name starts ME_. These are the ones you will actually use.
| Requirement | Hook (verified live) | Type | Notes |
|---|---|---|---|
| ⭐ "Block the save when our rule is broken" | ME_PROCESS_PO_CUST → method CHECK |
BAdI ✅ | The single most-used hook in MM. Interface IF_EX_ME_PROCESS_PO_CUST, Multiple Use, 13 methods: INITIALIZE · OPEN · PROCESS_HEADER · PROCESS_ITEM · PROCESS_SCHEDULE · PROCESS_ACCOUNT · ⭐CHECK (just before SAVE) · POST · CLOSE · 4 × FIELDSELECTION_*. SAP ships a worked example class CL_EXM_IM_ME_PROCESS_PO_CUST — read it in SE24. |
| "Make a field mandatory / hidden only in certain cases" | ME_PROCESS_PO_CUST → FIELDSELECTION_* | BAdI ✅ | Try plain field selection (OLME) first — it is rung 1. |
| "We need our own tab / our own fields on the PO" | ME_GUI_PO_CUST (screen) + MM06E005 (the data) | BAdI ✅ + user exit | Always used as a pair: the BAdI draws the screen, the exit stores the values. A genuinely common request. |
| "Change how the price is calculated" | ME_PO_PRICING_CUST | BAdI ✅ | Last resort — pricing is normally solved with condition types and calculation schemas (configuration). |
| "Do something after the PO is saved" (interface, e-mail, log) | ME_PURCHDOC_POSTED | BAdI ✅ | Fires after a successful save — the safe place for follow-on actions. |
| "Extra rules when choosing the source of supply" | ME_CHECK_SOURCES | BAdI ✅ | Additional checks in source determination. |
| "Turn off Hold / Park" | ME_HOLD_PO | BAdI ✅ | Small but frequently asked. |
| "Add a column to the PO list report" | ME_CHANGE_OUTTAB | BAdI ✅ | Enrich the ALV output in Purchasing. |
| "Our own document numbering rule" | MM06E003 — number range and document number | user exit | Only after you have proved SNRO cannot do it. |
| "Change the document overview in ME21N" | MEQUERY1 | user exit | — |
📝 Purchase requisitions
| Requirement | Hook (verified live) | Type | Notes |
|---|---|---|---|
| ⭐ "Check or change a requisition as it is entered" | ME_PROCESS_REQ_CUST | BAdI ✅ | The requisition twin of ME_PROCESS_PO_CUST — same idea, same method pattern. |
| "Our own fields on the requisition" | MEREQ001 — Customers' Own Data in Purchase Requisition | user exit | — |
| "Do something after the requisition is saved" | ME_REQ_POSTED | BAdI ✅ | — |
| "Force a buyer to re-approve after a change" | ME_REQ_APPROVAL | BAdI ✅ | Control of buyer approval after changes to a PR item. |
| "Create a new PR version on change" | ME_REQ_NEW_VERSION | BAdI ✅ | — |
| "Group requisitions differently when converting to POs" | ME590001 | user exit | PO split in ME59. |
📦 Goods movements & MIGO
| Requirement | Hook (verified live) | Type | Notes |
|---|---|---|---|
| ⭐ "Check or change a material document before it posts" | MB_DOCUMENT_BADI | BAdI ✅ | The goods-movement equivalent of the PO CHECK method. |
| "React when MSEG / MKPF are updated" | MB_DOCUMENT_UPDATE, or user exit MB_CF001 | BAdI ✅ / user exit | Update-time logic. Be careful — this runs inside the database update. |
| "We want our own tab inside MIGO" | MB_MIGO_BADI | BAdI ✅ | External detail subscreens in MIGO — the official way to add your own fields to the goods receipt. |
| "Change item data in MIGO on the fly" | MB_MIGO_ITEM_BADI | BAdI ✅ | — |
| "Extra rules on the quantity check" | MB_QUAN_CHECK_BADI | BAdI ✅ | — |
| "Default the storage location automatically" | MBCF0009 — Filling the storage location field | user exit | Try configuration first: the material master and movement-type settings can often do this. |
| "Our own text on the GR / GI slip" | MBCF0005 · MBCF0002 | user exit | Slip printing and segment text. |
| "Rules on reservations" | MB_RESERVATION_BADI, MBCF0007 | BAdI ✅ / user exit | MB21/MB22. |
| "GR default quantity / tolerance behaviour" | MEVME001 | user exit | Over- and under-delivery tolerance is configuration first. |
🧾 Invoice verification (MIRO)
| Requirement | Hook (verified live) | Type | Notes |
|---|---|---|---|
| ⭐ "Extra checks before the invoice is accepted" | MRM_HEADER_CHECK | BAdI ✅ | Additional checks of the document header data. |
| "Pre-fill fields when the invoice opens" | MRM_HEADER_DEFAULT · MRM_TRANSACT_DEFAULT | BAdI ✅ | — |
| "Different tolerance per supplier" | MRM_TOLERANCE_GROUP | BAdI ✅ | Standard tolerance keys (OMR6) come first — that is configuration. |
| "Set the payment terms by our own rule" | MRM_PAYMENT_TERMS | BAdI ✅ | — |
| "Extra checks before a blocked invoice is released" | MRM_RELEASE_CHECK | BAdI ✅ | — |
| "Spread unplanned delivery costs our way" | MRM_UDC_DISTRIBUTE | BAdI ✅ | — |
| "Our own fields on the invoice" | MRM_ITEM_CUSTFIELDS | BAdI ✅ | — |
| "Change ERS (self-billing) behaviour" | MRM_ERS_HDAT_MODIFY · MRM_ERS_IDAT_MODIFY · MM08R001 | BAdI ✅ / user exit | — |
🏷️ Material master
| Requirement | Hook (verified live) | Type | Notes |
|---|---|---|---|
| ⭐ "Refuse to save a material that breaks our data rules" | BADI_MATERIAL_CHECK | BAdI ✅ | Enhanced checks for material master tables. The clean way to enforce a data standard. |
| "Pre-fill defaults for new materials" | BADI_MATERIAL_REF | BAdI ✅ | Customer-defined default data. |
| "Our own checks / extra logic on the views" | MGA00001 — Material Master (Industry): Checks and Enhancements | user exit | The classic route. |
| "Our own material numbering rule" | MGA00002 — number assignment | user exit | Only after MMNR configuration has been proved insufficient. |
| "Add a whole new object/tab to the material" | BADI_MATERIAL_OD | BAdI ✅ | Larger job — usually a project in itself. |
✅ Release strategy (approval)
Release strategies are configuration (characteristics, class type 032, release groups and codes) — see Scenario 13. You only need these when the value the strategy looks at does not exist as a standard field.
| Requirement | Hook (verified live) | Type | Notes |
|---|---|---|---|
| ⭐ "Approve on a value SAP does not offer" (e.g. a custom field) | M06E0004 — changes to communication structure, purchasing docsM06B0002 — same, for requisitions | user exit | You add your field to the communication structure CEKKO/CEBAN, then use it as a characteristic like any other. |
| "Decide who approves, by our own rule" | M06E0005 (POs) · M06B0001 (PRs) | user exit | Role determination for release. |
| "Overall release of a requisition on a custom value" | M06B0005 | user exit | — |
🖨️ Output, labels and printed forms
Almost nothing here is a BAdI. Printing in SAP is output determination (configuration) plus a form (a developer or forms specialist). See Scenario 27 — Barcode Labels & Scanning for the whole chain worked through.
| Layer | Where | Who | What you do there |
|---|---|---|---|
| Decide when something prints | NACE → application (EF = purchase order, ME = inventory management, MR = invoice verification) | MM | Output types, procedures, access sequences — pure configuration. |
| Decide for whom it prints | MN21 / MN04 — condition records | MM | Master data, no code. |
| What the paper looks like | SMARTFORMS / Adobe Forms | ABAP | You supply the layout and the field list; a specialist builds it. |
| Barcodes on that paper | SE73 — system barcodes | BASIS | SAP already ships Code 128, Code 39, EAN-13, QR — usually nothing to create. |
| Where it physically prints | SPAD — printers | BASIS | — |
⚖️ Step 5 — Deciding, and saying it out loud
- "Standard can do most of this." — name the setting, and say how much of the need it covers.
- "The rest needs an enhancement, and here is the exact hook." — name it, and say you confirmed it is usable on this system.
- "Here is what it will cost you forever." — testing at every upgrade, one more thing that can break, one more person who must understand it.
EXIT_SAPLV01Z_002 — which was not even usable there, because the customer function group had never been generated. I advised the client that numeric bands per material type met about 90% of the need at zero risk, and that the custom prefix would add a permanent upgrade cost for a cosmetic benefit."
That answer shows config knowledge, enhancement knowledge, and commercial judgement — which is exactly what the question is testing.
🔎 The four searches — keep these
| To find… | Do this |
|---|---|
| A BAdI by topic | SE16 → SXS_ATTRT → SPRSL=E, EXIT_NAME=*keyword* → read the description, then SE18 to open it |
| A classic user exit by topic | SE16 → MODSAPT → SPRSL=E, NAME=M* → then SMOD → Components |
| Which enhancement owns a function exit | SE16 → MODSAP → MEMBER = the EXIT_… name |
| Whether an exit really exists | SE16 → TFDIR → FUNCNAME = the EXIT_… name |
💡 These four work in every SAP module, not just MM. They are probably the most transferable thing on this whole site.