Below is the exact code I am using at the moment - this is a copy of code I am using in the FEEDER_FORM class which works as desired.
zmdgm_ui_config is a custom table containing lists of fields against CR types and Step numbers as well as the fields from ct_field_usage (for example read_only and visibility) and is a way to dynamically set the field usage
* Get Read-only Access to USMD Model Data & Retrieve Change Request Info
call method cl_usmd_model_ext=>get_instance
exporting
i_usmd_model = 'MM'
importing
eo_instance = lr_model.
lo_app_context = cl_usmd_app_context=>get_context( ).
if not lo_app_context is initial.
lo_app_context->get_attributes(
importing
ev_crequest_type = lv_crequest_type
ev_crequest_step = lv_crequest_step ).
endif.
* Loop Through Field Usage Parameter for Each Field
sort ct_field_usage by name ascending.
lt_field_usage = ct_field_usage.
loop at lt_field_usage into ls_field_usage.
* Change the field usage based on UI config table
select single *
from zmdgm_ui_config
into ls_zmdgm_ui_config
where crtype eq lv_crequest_type
and step eq lv_crequest_step
and name eq ls_field_usage-name.
if sy-subrc = 0.
ls_field_usage-visibility = ls_zmdgm_ui_config-visibility.
ls_field_usage-read_only = ls_zmdgm_ui_config-read_only.
ls_field_usage-mandatory = ls_zmdgm_ui_config-mandatory.
ls_field_usage-enabled = ls_zmdgm_ui_config-enabled.
* Retrieve index of Parameter and change the appropriate row according to UI Config Table
read table ct_field_usage
with key name = ls_zmdgm_ui_config-name transporting no fields
binary search.
modify ct_field_usage index sy-tabix from ls_field_usage.
endif.
endloop.
* Set Parameter to Changed
ev_field_usage_changed = abap_true.