Simplifying Complexity in SAP MDG-F: Dynamic Field Control via Custom Rule Engine | Reimagining SAP MDG Field Validations Low-Code Approach | UI Field Control Made Dynamic Yet Simple !
Validate master data entries against business rules to ensure they are accurate, complete, and consistent.
Working with SAP MDG (Master Data Governance) often means balancing two realities: robust governance and agile adaptability. One of the common we face during MDG implementation is managing field validations with user-friendly alerts.
Whether a field should be hidden, mandatory or optional often depends on multiple factors like CR type, entity, company code or user role etc. Traditionally, these validations are implemented via BRF+ expressions, Enhancements in feeder classes, Hard-coded logic in the UI layer/BAdIs.
MDG validations are crucial for maintaining clean and consistent master data. However, the standard methods come with limitations & most teams rely on:
- Feeder class logic: Feeder class enhancements require ABAP expertise and testing cycles.
- UI rule maintenance is scattered and often difficult to track.
- BRF+ rules or hardcoded validations code: Every new business scenario (e.g. different behaviour based on company code or region) means new logic and testing.
- No centralized view of how validations are controlled across the application.
- While these are functional, they’re rigid, developer-dependent, and non-scalable. This complexity makes it difficult for functional/ business teams to adapt validations quickly and every change creates a dependency on development teams reducing agility.
- Hide/Show fields based on Role UI logic is non-transparent.
- Maintain field rules over time - Static logic -No visibility for business teams.
- The key pain point: Every small validation change turns into a development cycle.
- But what if we could avoid coding and development cycle every time specially for simple level properties or validations, yet still provide fully dynamic, context-sensitive field behaviour.
To address these challenges, a custom configuration table was designed to serve as a central validation engine covers easy level validations. Field behaviour across various MDG entities and scenarios was defined through this table eliminating the need for code changes every time.
- These rules are consumed at runtime ensuring the UI responds dynamically based on master data context.
- Validation Types : M = Mandatory, O = Optional, H = Hidden, R = Read Only
- This table is maintained via a simple maintenance view or SM30 transaction/T-Code assigned to TMG accessible to key users or functional consultants.
All this happens without modifying feeder classes or enhancing the UI every time.
- No More Coding: New validation rules don’t require any ABAP changes at-least for simple validations.
- Business Empowerment: Functional/Business teams can maintain rules via a user-friendly view.
- Central Governance: One place to view and manage all field control rules along with by whom & when it’s added or changed.
- Audit-friendly: clearly see why and when a rule applies.
- No BRF+ overload: clean separation of decision logic.
- Faster Change Cycles: No transports or regression testing required for every new validation rule.
- Scalable to all MDG domains MDG – S,M,C (Supplier, Material, Customer)
- After rollout: Reduced ~70% of validation-related change requests.
- Built an enterprise-grade, reusable framework used across modules.
- Delivered faster time-to-compliance for changing regulatory/market needs.
- It's a shift from “write code to validate” ➝ to “configure to adapt”
- Custom Table is maintained in SM30 or Z/Y T-code (or via custom UI).
- Lightweight enhancement logic is added once to retrieve rules based on runtime values.
- The UI behaviour adjusts: field becomes hidden, read-only mandatory or optional.
- No need to touch feeder classes again & ever.
- Workflow context (field is mandatory only in approval steps)
- User or Role-level personalization (validation based on user group)
- For multiple conditions can add different Sr. No conditions. E.g. Department is mandatory for Company Code = ‘0001’ & '0005', So Can add two records with different Sr. No.
- Business Area to be hidden.
- Profit Center is mandatory.
- Department is mandatory for Company Code = ‘0001’
- User Responsible is mandatory for cost center category = ‘%’.
[ Fig. 1.0. Table Contents ]
- ZCCT1P2: Create Cost Center with Hry. Assignments.
- Step ‘0’ defines Requestor Level.
- Tech Property & Conditional Tech-Property stores technical attributes.
- Table also contains audit columns to track changes.
- EI: ZFI_ES_MDG_FLD_PROP
- BAdI Implementation: ZFI_MDG_FLD_PROP
- Class: ZCL_FI_MDG_FLD_PROP
- Method: IF_EX_USMD_ACC_FLD_PROP_CDS~MODIFY_FLD_PROP_ATTR
METHOD if_ex_usmd_acc_fld_prop_cds~modify_fld_prop_attr.
DATA:lr_model TYPE REF TO if_usmd_app_context.
CALL METHOD cl_usmd_app_context=>get_context
RECEIVING
eo_context = lr_model.
IF lr_model IS BOUND.
CALL METHOD lr_model->get_attributes
IMPORTING
ev_crequest_type = DATA(lv_cr_type)
ev_crequest_step = DATA(lv_cr_step).
ENDIF.
DATA: go_tab_descr TYPE REF TO cl_abap_tabledescr,
go_struc_descr TYPE REF TO cl_abap_structdescr,
lt_components TYPE abap_component_tab.
DATA: name_range TYPE RANGE OF zfi_mdg_valt-condition_tech.
"Pre-Exception Handling To Get Valid Fields For it_data As not-valid entry entered in Condition Property
go_tab_descr ?= cl_abap_tabledescr=>describe_by_data( it_data ).
CHECK sy-subrc = 0.
go_struc_descr ?= go_tab_descr->get_table_line_type( ). "Get the structure of your internal table
lt_components = go_struc_descr->get_components( ).
REFRESH name_range.
name_range = VALUE #( FOR wa IN lt_components ( sign = 'I' option = 'EQ' low = wa-name ) ).
"Check Mandatory(*)/Hide/Read Only/Optional Attribute Property
SELECT * FROM zfi_mdg_valt INTO TABLE (it_mdg_val)
WHERE cr_type = _cr_type AND cr_step = _cr_step AND entity_type = _entity
AND zmodule = '0G' AND active = 'X'.
IF sy-subrc = 0.
LOOP AT ct_fld_prop ASSIGNING FIELD-SYMBOL(<fs_data_2>).
ASSIGN COMPONENT 'USMD_FP' OF STRUCTURE <fs_data_2> TO FIELD-SYMBOL(<fs_fld_prop_2>).
LOOP AT it_mdg_val INTO DATA(wa_val) WHERE entity_type = iv_entity. "Traverse Custom Defined Properties
IF wa_val-condition_tech IS NOT INITIAL. "Check If Condition maintained or free of any condition
IF wa_val-condition_tech IN name_range. "Preventive Action
READ TABLE it_data ASSIGNING FIELD-SYMBOL(<fs_data_con>)
WITH KEY (wa_val-condition_tech) = wa_val-condition_val.
"Condition is not matching, continue with next iteration of properties of it_mdg_val
IF sy-subrc NE 0.
CONTINUE.
ENDIF.
ELSE.
CONTINUE. "Not Valid Condition Attribute/Property
ENDIF.
ENDIF.
"Check for further tech property
ASSIGN COMPONENT wa_val-tech_name OF STRUCTURE <fs_fld_prop_2> TO FIELD-SYMBOL(<ls_field_2>).
IF <ls_field_2> IS ASSIGNED.
<ls_field_2> = wa_val-property.
ENDIF.
UNASSIGN <ls_field_2>.
ENDLOOP.
UNASSIGN <fs_fld_prop_2>.
ENDLOOP.
ENDIF.
ENDMETHOD.- EI: ZFI_ES_MDG_VAL
- BAdI Implementation: ZFI_MDG_VAL
- Class: ZCL_FI_MDG_VAL
- Method: IF_EX_USMD_RULE_SERVICE~CHECK_ENTITY
METHOD if_ex_usmd_rule_service~check_entity.
DATA: lr_model TYPE REF TO if_usmd_app_context.
DATA: go_tab_descr TYPE REF TO cl_abap_tabledescr,
go_struc_descr TYPE REF TO cl_abap_structdescr,
lt_components TYPE abap_component_tab.
DATA: name_range TYPE RANGE OF zfi_mdg_valt-condition_tech.
CALL METHOD cl_usmd_app_context=>get_context
RECEIVING
eo_context = lr_model.
IF lr_model IS BOUND.
CALL METHOD lr_model->get_attributes
IMPORTING
ev_crequest_type = DATA(lv_cr_type)
ev_crequest_step = DATA(lv_cr_step).
ENDIF.
"Pre-Exception Handling To Get Valid Fields For it_data As not-valid entry entered in Condition Property
go_tab_descr ?= cl_abap_tabledescr=>describe_by_data( it_data ).
CHECK sy-subrc = 0.
go_struc_descr ?= go_tab_descr->get_table_line_type( ). "Get the structure of your internal table
lt_components = go_struc_descr->get_components( ).
REFRESH name_range.
name_range = VALUE #( FOR wa IN lt_components ( sign = 'I' option = 'EQ' low = wa-name ) ).
"Mandatory Check Working for FI : GL,CC,PC
SELECT * FROM zfi_mdg_valt INTO TABLE (it_zfi_mdg_valderv)
WHERE cr_type = _cr_type AND cr_step = _cr_step
AND zmodule = '0G' AND property = 'M' AND active = 'X'.
LOOP AT it_zfi_mdg_valderv INTO DATA(wa_val).
LOOP AT it_data ASSIGNING FIELD-SYMBOL(<ls_data>).
IF <ls_data> IS ASSIGNED.
IF wa_val-condition_tech IS NOT INITIAL. "Check If Condition maintained or free of any condition
IF wa_val-condition_tech IN name_range. "Preventive Action
ASSIGN COMPONENT wa_val-condition_tech OF STRUCTURE <ls_data> TO FIELD-SYMBOL(<ls_con>).
IF <ls_con> IS ASSIGNED AND <ls_con> IS NOT INITIAL.
IF <ls_con> NE wa_val-condition_val.
CONTINUE.
ENDIF.
UNASSIGN <ls_con>.
ENDIF.
ELSE.
APPEND VALUE #( msgty = 'E' msgv1 = 'Please check entity name' msgv2 = wa_val-condition_tech ) TO et_message.
CONTINUE.
"msgid = 'zfi_mdg_message' msgno = 000
ENDIF.
ENDIF.
ASSIGN COMPONENT wa_val-tech_name OF STRUCTURE <ls_data> TO FIELD-SYMBOL(<ls_value>).
IF <ls_value> IS ASSIGNED AND <ls_value> IS INITIAL.
APPEND VALUE #( msgid = wa_val-msgid msgty = wa_val-msgty
msgno = wa_val-msgno msgv1 = wa_val-msgv1 msgv2 = wa_val-msgv2 ) TO et_message.
UNASSIGN <ls_value>.
ENDIF.
ENDIF.
ENDLOOP.
ENDLOOP.
ENDMETHOD.1.1. With Loading, Business area is hidden, Profit Center is mandatory, Department & User Responsible are not mandatory.
[ Fig 1.1 : Initial Screen ]
1.2. With entering Company code = ‘0001’, Department becomes mandatory (*).
[ Fig 1.2 : Department is mandatory ]
2.1. For Company code = ‘0003’, Department is not mandatory
[ Fig 2.1 : Department not mandatory ]
3.1. With loading, User Responsible is not mandatory by default.
[ Fig 3.1 : User Responsible not mandatory ]
3.2. With Cost center category = ‘%’, User Responsible becomes mandatory.
[ Fig 3.2 : User Responsible mandatory ]
3.3. Click on Check: As Profit Center & User Responsible are blank, we got error messages defined in our Z table.
[ Fig 3.3 : Messages ]
3.4.Errors on Check
[ Fig 3.4 : Messages ]
To troubleshoot scenario where user has maintained conditional-tech-property/attribute as not-valid property value ( By mistake ; ) So below error will appear on hitting Check.
As we are using this conditional-tech-property to read IT_DATA, it’s safe idea to check pre-hand whether the property is valid and actually present in IT_DATA to refrain from last moment surprises. (or technically a dump ; )
[ Fig. 4.1. Preventive Code ]
[ Fig : 4.2. conditional tech attribute = ‘CCODECCTR1’ is not valid attribute of data model ]
[ Fig : 5 Message Class ]
- SAP MDG is built to empower governed, agile master data. But flexibility shouldn’t mean fragility. With a configurable validation engine, we’ve transformed how we think about field behavior turning a developer-led task into a business-led control mechanism and maximum governance.
Thanks !












