Top Enhancement Spots and BAdIs in SAP TM Every ABAPer Should Know
Every SAP TM project reaches the same point. The customer wants freight units built differently for one plant, a surcharge calculated from a field that standard does not know, or a freight order printout that only goes out when a custom condition is met. The developer searches SE18 and gets a long list of /SCMTMS/ definitions with little documentation.
This post is the short list we wish we had on our first TM project: the enhancement points we actually used, the area each one belongs to, and a pattern that keeps custom code from breaking planning or charge calculation. Names change between TM 9.x and the S/4HANA embedded releases, so treat every object below as a starting point to confirm in SE18 on your own system.
1) How to find the right BAdI yourself
Lists go out of date; a search method does not. We use these four techniques, in this order:
- SPRO first. TM customizing has a dedicated node for Business Add-Ins under SAP Transportation Management. Each entry there opens the BAdI documentation and the implementation screen. The exact node path differs between releases, so verify it in your release.
- SE18 / SE84 with a name pattern. In SE84, go to Enhancements > Business Add-Ins > Definitions and search for
/SCMTMS/*. Combine this with a keyword such asFUB,TCC(charge calculation) orPLN(planning) to narrow the result. - Debugger breakpoint at statement. In the ABAP debugger, set a breakpoint at the statements
GET BADIandCALL BADI, then run the business transaction. Every new-style BAdI the process touches will stop the debugger, and you can see the calling class and the data available at that moment. - Error messages as clues. Messages such as “Optimizer run canceled in preprocessing BAdI” (message class
/SCMTMS/PLN_VSR_IL) tell you that a hook exists at exactly that spot.
2) BAdI or BOPF enhancement? Decide before you code
SAP TM is built on BOPF, so many requirements are better solved with a custom determination, validation or action enhancement on /SCMTMS/TOR or /SCMTMS/TRQ. You create these in the BOPF Enhancement Workbench (transaction /BOBF/CUST_UI). A BAdI is the better choice when SAP has given you a hook inside a process engine, such as planning or charge calculation, where BOPF changes would arrive too early or too late.
| Requirement | Preferred technique | Why |
|---|---|---|
| Fill or derive a field on freight order save | BOPF determination (enhancement of /SCMTMS/TOR) | Runs for every channel: UI, Fiori, background, integration |
| Block saving when data is inconsistent | BOPF validation | Messages appear in the standard message log |
| Add logic before or after a standard action | Pre/post action enhancement | No modification of the standard action class |
| Change input or result of the optimizer | Planning BAdI | Only point where optimizer data is exposed |
| Custom value for a calculation base | Charge calculation BAdI | The engine asks for the value at the right moment |
| Control when output is triggered | PPF condition BAdI | Output in TM runs through PPF |
Pitfall
Do not enhance UI feeder classes to implement business logic. That logic then runs only in that one screen, and background jobs or integration messages skip it.
3) Freight Unit building
Freight Unit building is driven by the FUB rule. Most requirements can be met with conditions in the FUB rule before you write any code. When that is not enough, two hooks are worth knowing:
/SCMTMS/TRQ_CHECK_FUB: by default, only changes to standard nodes of/SCMTMS/TRQtrigger freight unit building again. If you added custom nodes to the transportation requirement and changes there must rebuild freight units, this BAdI lets you include them. SAP Knowledge Base Article 2602332 describes this case for TM 9.1 and later.- Planning methods
FUB_PRE,FUB_POSTandFUB_AUTOin class/SCMTMS/CL_PC_METHODSare reported in SAP Community as places to add logic before or after freight unit creation through an enhancement. This is not an official BAdI, so verify in your release that the methods exist and are called in your scenario.
4) Charge calculation
Charge Management has the most useful hooks for consultants, because pricing requirements are where standard most often falls short.
/SCMTMS/TCC_BO_DATA_ACCESS, methodGET_CALC_BASE_VALUES: use this to supply a value for a calculation base that standard data access cannot read, such as a custom field or a value derived from several nodes. In the calculation base customizing you also have to set the BAdI flag, otherwise the method is never called. We covered the data access side in an earlier post on creating a new calculation base.- Charge engine BAdI (interface
/SCMTMS/IF_TCC_ENGINE_BADI, methodPROCESS_CALCULATION_SHEETS): SAP Community reports this method being called on recalculation and on save, which makes it the place to add or adjust charge lines after the calculation. Verify in your release which BAdI definition implements this interface. - Charge calculation rules and formulas: for rate logic that a rate table cannot express, the rule and formula BAdIs are worth checking. Error messages in message class
/SCMTMS/TCC_MSGrefer to BAdI/SCMTMS/CHARGE_CALCULATION. Verify its methods in SE18 before you design around it.
Pitfall
Calculation base BAdIs run for every charge line of every document. Avoid single SELECTs inside loops. Buffer master data per call, and test with the charge calculation log turned on.
5) Planning and the optimizer
/SCMTMS/PLN_OPT is the BAdI for VSR optimization. It has one method that changes the data sent to the optimizer and one that changes the result before TM applies it. SAP has described using it to model via-locations by adding extra distance, duration and cost to the distance data, because the optimizer does not handle via-locations natively. For load planning, SAP documented a separate enhancement spot, /SCMTMS/PLN_OPT_LSO, which is available from TM 9.1 SP01.
Solution tips
- Change only the data you need. Removing constraints in preprocessing quietly turns an optimal plan into a wrong one.
- Any unhandled exception cancels the whole optimizer run. Users then see messages
/SCMTMS/PLN_VSR_IL010or011without knowing why.
6) Output management (PPF)
TM output runs through the Post Processing Framework. You maintain the action profiles in SPPFCADM, and TM uses /BOFU/CL_PPF_CONTAINER as the PPF object type. The hooks are standard PPF BAdIs, not TM-specific ones:
EVAL_STARTCOND_PPFandEVAL_SCHEDCOND_PPF: start and schedule conditions, for example “print only when the carrier has confirmed”.COMPLETE_PROC_PPF: complete processing data at runtime, for example to set email recipients from the business partner.
Standard ships no conditions, so if output fires too often, check first whether any condition is assigned at all.
7) Integration with ERP / S/4HANA order processing
On the sending side, SD and MM provide BAdIs for sales document, purchase order, delivery and sales order scheduling integration with TM. You can use them to change the control key that decides whether a document goes to TM. For purchase orders the BAdI is BADI_LO_TM_PUR. On the TM side, /SCMTMS/BADI_LI_BTD_CHG (for example method ADAPT_LI_INPUT_MM) adjusts the incoming data. One caveat: an SAP KBA documents that in S/4HANA 2021 and later this method cannot change the PO control key. Do not plan a “skip integration per document” design around it. Use the sending-side BAdI instead.
8) A safe implementation pattern
Whatever the hook, we wrap every TM BAdI implementation the same way: a kill switch, a separate logic class, and no unhandled exceptions. If the custom logic fails, the standard process must still finish.
METHOD <badi_interface>~<method>.
" 1) Kill switch: deactivate in production without a transport
SELECT SINGLE low FROM tvarvc
WHERE name = 'ZTM_BADI_FUB_CHECK' AND type = 'P'
INTO @DATA(lv_active).
IF lv_active <> abap_true.
RETURN.
ENDIF.
" 2) Logic lives in its own class: unit-testable, reusable
TRY.
NEW zcl_tm_fub_rules( )->evaluate( ... ).
CATCH cx_root INTO DATA(lx_error).
" 3) Never dump inside planning or charge calculation:
" log the error and let standard continue
zcl_tm_app_log=>add_exception( lx_error ).
ENDTRY.
ENDMETHOD.
When the logic needs freight order data, read it through the BOPF service manager (/bobf/cl_tra_serv_mgr_factory=>get_service_manager( /scmtms/if_tor_c=>sc_bo_key )), not with direct SELECTs on the database tables. That way you also see unsaved changes. Never call COMMIT WORK inside a BAdI. The surrounding transaction owns the commit. For larger kill-switch logic, a BRF+ decision table is a cleaner option than TVARVC, as described in our earlier post.
Takeaways
- Learn to find hooks with SE84, the SPRO BAdI node and a
GET BADIbreakpoint. That outlasts any list. - Prefer BOPF determinations, validations and action enhancements for document logic. Use BAdIs where a process engine (FUB, charges, optimizer, PPF, integration) gives you one.
- Wrap every implementation with a kill switch and exception handling so custom code can never stop planning or settlement.
- Confirm every name in SE18 on your own release before you design around it.
Which TM BAdI saved your project, or let you down? Share it in the comments.