QPPD
QPPD serves as a data modeling tool for master data and can also function as a central data hub hosting complex objects based on configuration values, such as the "technical order" for order dressing. This page explains how to link QPPD elements to characteristics and/or link QPPD objects to a configuration instance (both ways). Enhance your configuration logic and behavior with QPPD to elevate it to the next level.
Characteristic Links
When using QPPD to maintain master data, both elements and their values are typically managed directly within QPPD.
In the AVC context, a characteristic must exist beforehand to be included in a configuration model. For example, the characteristic PRODUCT_FORM
needs to be created and added to the model.
Maintaining characteristic values in both AVC and QPPD is inefficient and error-prone. Therefore, QPPD should act as the single source of truth.
To support this, mdVC includes a pre-delivered implementation of the BADI vch_hl_es_core-vch_hl_md_domain_modify
, which uses the class /mxp/vcha_cl_qppd_domain_modif
. This implementation checks whether a matching QPPD element exists for a given characteristic and, if so, supplies the QPPD-defined values for configuration use at runtime.
Make sure to define suitable filter values when setting up the BADI.
With this setup, there is no need to duplicate value maintenance in the characteristic itself — QPPD remains authoritative. However, the characteristic still needs to exist in the system to be added to the configuration model.
Specification Links
For complex calculations such as order dressing, calculating production alternatives, or other computations, it is beneficial to integrate and mirror some AVC values into QPPD in a separate Specification usually called a "Technical Order." This integration enables seamless, bidirectional synchronization of characteristic values between AVC and QPPD while ensuring configuration logic remains consistent and centrally governed in mdVC by reusing QPPD logic.
Due to the stateless nature of AVC and to allow users to modify specification values in QPPD, sophisticated draft handling is necessary. The draft handling described here focuses on sales documents where it's mandatory (sales document processing in SAP is stateful, AVC is stateless).
Synchronization Process
The following sections focuses on the key steps related to QPPD synchronization.
Synchronization Trigger and Target
The initial synchronization is a task passed to the engine:
new /mxp/vcha_cl_qp_va_sync( )
In the standard implementation, log messages are synchronized back to AVC. Consider the Best practice to derive status after QPPD synchronization completes.
The enhancement spot /mxp/vcha_es_qp_sync
controls synchronization: configurations for QPPD specification types, synchronization object type definitions, and status definitions.
Document Configuration
To prevent issues like late numbering and manage "out-of-date" draft objects, two custom fields are required at the SAP sales order header level:
-
Header Id
A "Character 22" field storing a unique header ID. It is filled immediately after sales order creation and never changed. -
Activation Session Timestamp
A timestamp field filled at the start of a sales order session. Internally used by the QPPD draft implementation to detect outdated drafts. Must be of typetimestampl
.
Best practice in S/4 systems is to use the "Custom Fields" app (#CustomField-manage
). For older releases, extend via developer extensibility. Only the Custom Fields app is considered clean-core compliant here.
Recommended field names are ZZ1_HeaderId
and ZZ1_ActivationSession
. If these conflict with your naming conventions, implement the BADI /mxp/vcha_doc_link_config
to configure custom fields.
The logic for these fields is delivered with mdVC, but the fields themselves are not (to remain clean core compliant).
The method get_header_fields
must be implemented:
method /mxp/vcha_if_doc_link_config~get_header_fields.
e_header_id = 'ZZ1_HeaderId' ##NO_TEXT.
e_activation_session_ts = 'ZZ1_ActivationSession' ##NO_TEXT.
endmethod.
Synchronization Configuration
The target QPPD object must be defined. The BADI /mxp/vcha_qp_sync_vart_config
configures the specification type, target object type, and follow-up actions on the QPPD object after synchronization. The method get_configuration
must be implemented; configuration can be plant-specific.
Specification Type Contract
- No Specification Group should be assigned in Specification Type Customizing.
- An Object Type at header level must be assigned, listing all elements whose characteristics need synchronization from AVC instance. The names must match.
All configuration values synchronize first to this object type, triggering follow-up actions. - Builder, Generation, and Follow-Up functions can be freely defined in QPPD style and configured within the BADI. They execute whenever a relevant synchronization element changes.
- The status schema must follow the Status Configuration described below.
Method Implementation
The implementation may be plant-specific. A default implementation is provided:
-
Specification Type:
Defaults to the QPPD Technical Order looked up by abbreviationTO
in Specification Type Customizing. -
Object Type:
Default implementation links toMDVC_CONFIG
; keep this name if using the default. -
Key Parts VC:
The QPPD object is linked by its Name. The default derives the name from a characteristicMDVC_LINK_VNAME
. This value must be determined by a task before the actual QPPD synchronization. This is where the configured header ID is used.-
VBAK_Z_UUID
(customizable name) holds the header ID defined in Document Configuration, made available to AVC since it only accesses configuration values.
-
POSNR
(customizable name) holds the sales order item number, also exposed to AVC.
-
MDVC_LINK_VNAME
is calculated by a custom task.
-
Example task configuration showing that lcl_calc_mdvc_link_vname
runs before QPPD synchronization:
/mxp/vcha_cl_va_engine=>factory(
i_badi_implementation = me
i_explosion_date = ref #( explosion_date )
i_hierarchy = ref #( hierarchy )
i_characteristics = ref #( characteristics )
i_values = ref #( values )
i_bill_of_materials = ref #( bill_of_materials )
i_assign_by_value = ref #( assign_by_value )
i_assign_by_key = ref #( assign_by_key )
i_unassign = ref #( unassign )
)->process( new /mxp/vcha_cl_va_task_composite( value #(
( new lcl_calc_mdvc_link_vname( ) )
( new /mxp/vcha_cl_qp_va_sync( ) )
( new /mxp/vcha_cl_va_log_to_status( ) ) ) ) ).
Example implementation of lcl_calc_mdvc_link_vname
:
class lcl_calc_mdvc_link_vname definition create public inheriting from /mxp/vcha_cl_va_task_simple.
public section.
methods /mxp/vcha_if_va_task~execute redefinition.
endclass.
class lcl_calc_mdvc_link_vname implementation.
method /mxp/vcha_if_va_task~execute.
data(l_z_uuid) = i_engine->m_container->get_assigned_value_for_cstic(
i_engine->m_container->get_characteristic_by_name( i_instance_id = i_engine->m_container->m_root_instance_id
i_cstic_name = 'VBAK_Z_UUID' )->key ).
data(l_posnr) = i_engine->m_container->get_assigned_value_for_cstic(
i_engine->m_container->get_characteristic_by_name( i_instance_id = i_engine->m_container->m_root_instance_id
i_cstic_name = 'POSNR' )->key ).
check l_z_uuid is bound and l_posnr is bound.
i_engine->m_container->assign_value( value #( base corresponding #( i_engine->m_container->get_characteristic_by_name( i_instance_id = i_engine->m_container->m_root_instance_id
i_cstic_name = 'MDVC_LINK_VNAME' )->key )
value_string = /mxp/vcha_cl_qp_sd_key_util=>instance->key_for_item(
i_header_id = l_z_uuid->value_string
i_item = conv #( l_posnr->value_int_min ) ) ) ).
endmethod.
endclass.
You can skip calculating MDVC_LINK_VNAME
and instead concatenate via BADI settings (simpler but less flexible). At least a separate characteristic is needed for the AVC UI extension which is why we decided to recommend this way.
- Define follow-up actions executed after any relevant configuration value change (object created or modified):
- Builder flag: whether to execute QPPD Builder
- Generation flag: whether to suppress QPPD Global Generation
- Action name: which specific VART function to execute (see QPPD docs)
Status Configuration
Multiple instances of a QPPD object for a configuration instance can exist. The objects are not versioned but are distinguished by their status. All share the same Name, as defined in Synchronization configuration. The BADI /mxp/vcha_qp_draft_status_conf
is used to configure the different statuses.
A default implementation is provided by /mxp/vcha_cl_qp_draft_stat_cfg
.
-
Draft object
The draft object exists "within a configuration" and can be discarded or adapted on each change.
The default implementation creates the object with statusIZQER
(in creation). -
Draft object "to be released"
A draft object moves to "to be released" after the "Done" button is pressed in AVC, meaning it will be saved when the sales order is saved.
The default implementation configures activityZQFB
("release requested") to be executed, setting statusIZQFB
according to status customizing. -
Active object
A draft object "to be released" is promoted to "active" after saving the sales order as a post-processing step.
Sales Order change events are fired by SAP standard, triggering class/mxp/vcha_cl_qp_slo_evt_activ
as standardBGPF
.
The default implementation configures activityZQFR
("release") to be executed, setting statusIZQFR
according to status customizing. -
Inactive object (previously active, but replaced)
An active object is marked inactive if another draft object "to be released" becomes active.
The default implementation configures activityZQLO
("mark for deletion") to be executed, setting statusIZQLO
according to status customizing.
The default implementation must match the status schema of the configured Specification Type:
method /mxp/vcha_if_qp_draft_stat_cfg~get_configuration.
r_result = value #( draft = value #( status = /sct/qp_cl_const=>status-in_creation
pending = value #( status = /sct/qp_cl_const=>status-release_requested
activities-promote_to_active = /sct/qp_cl_const=>activity-release )
activities = value #( initialize = value #( )
promote_to_pending = /sct/qp_cl_const=>activity-request_release ) )
active = value #( status = /sct/qp_cl_const=>status-released
inactive = value #( status = /sct/qp_cl_const=>status-marked_for_deletion )
activities = value #( deactivate = /sct/qp_cl_const=>activity-delete ) ) ).
endmethod.
Synchronization Back
Because the structure of a source QPPD object may vary a lot between use cases, no BADI is provided currently for synchronization back. Instead, the method SYNCH_BACK_TO_VC
in the task class /mxp/vcha_cl_qp_va_sync
is available for redefinition according to custom use cases. Additionally, all error messages within the object hierarchy will be synchronized to the AVC log, influencing status in the default handling (task /mxp/vcha_cl_va_log_to_status
).
Finish Configuration
Once the configuration is finished by clicking Done, the AVC BADI vch_hl_on_save
is triggered. The class /mxp/vcha_cl_qp_on_save
can be added for a BADI implementation. It handles promoting the draft to the status "to be released".
This status serves as an intermediate state while processing the sales document.
Post-Processing: Release QPPD Draft
As a post processing after saving the sales documents, all linked objects in status "to be released" will be promoted to "released". The Activation Session Timestamp will be considered, so that only relevant ones will be promoted:
- The Activation Session Timestamp will be calculated once an object in status "to be released" exists for the document, but the timestamp is the beginning of the editing session, not the current one.
- On Save, all objects which "last update" timestamp is higher than the Activation Session Timestamp will be considered as "to be promoted".
Objects which have errors will not be promoted to released nor considered as obsolete. They wait for manual editing (as the configuration does).
- The relevant objects will be changed to "released".
If any error occurs, the standard BGPF
unit will fail. Details are available in the standard transaction SBGRFCMON
. For more sophisticated logic, consider using an UBC interface instead.
Delete obsolete Objects
Scheduled as a regular job, the previously mentioned conditions will be checked and all drafts older than the specified lifetime will be deleted (e.g., incomplete configurations, cancelled editing sessions, etc.).
- Open the Fiori App for Application Jobs (#ApplicationJob-show).
- Create a new Application Job based on the template
/mxp/vcha_qp_draft_cleanup
.
- Choose a recurrence pattern; our recommendation is to schedule it once a day, which is sufficient.
- Choose a lifetime of drafts in seconds; our recommendation is about
86,400
(one day), meaning all obsolete drafts older than a day will be deleted. - The specification type will be defaulted with all your specification types according to the BADI implementation. From a default implementation perspective, this means "all Technical Orders".
UI Extension
AVC provides a powerful UI extension approach, enabling, for example, the display of linked QPPD objects as a dedicated tab "group" in AVC. For more information on AVC UI extensions, see SAP Help and GitHub.
In brief, to display information of the linked QPPD object, the following configuration/development steps are required:
- Develop your UI extension app as linked in the GitHub Repository.
- Register an AVC UI extension and assign a name with Fiori app
#VarCnfUserInterfaceExtension-manage
- Define the characteristic groups for your configuration model in Fiori app
#VariantConfiguration-manageGroups
and assign characteristics.- Make sure to add at least one characteristic; otherwise, the tab remains hidden.
- It is recommended to calculate the linked specification name as a separate characteristic, as mentioned in Synchronization configuration, and add it to this group. The UI extension can then access this value and display the object's values by filtering on it.
- Link the UI extension to this group by specifying your View Extension name in group header.
- Assign the characteristic group to the configuration profile in Fiori app
#VariantConfiguration-assignGroupsToProfile
.
For implementing your AVC UI extension:
-
Best practice is to expose parts of the QPPD object with OData and use Fiori Elements for displaying it.
Consider using GDD to generate semantic-rich CDS views as a basis for the custom Consumption View. -
Be aware that draft objects must not be displayed in display mode, only in editing mode.
-
Unfortunately, it is not supported from the AVC UI Extension side to embed external UI5 libraries, thus it is currently not possible to offer a library for helper functions such as:
- Jump to QPPD
- Display QPPD objects inline: UI and draft handling
- ...
Nevertheless, metalsXP has some content available "pre-packaged". The current suggestion is to copy it as a "quick start" to your custom Extension project. This may change in the future if libraries become supported.
💬 Please reach out to the team of metalsXP for further details.
Setup Summary
- Add custom fields for "Header Id" and "Activation Session Timestamp" as described in Document configuration.
- Perform QPPD and configuration customizing as described in Synchronization configuration and Status configuration.
- Add the synchronization task to QPPD as per Synchronization configuration.
- Implement the BADI for promoting the draft as described in Finish Configuration.
- Schedule the regular job for deleting obsolete objects as described in Delete obsolete Objects.