In the specification type class, functions are implemented for generating the PP-specific structures for BOMs and routings. Examples of some functions are shown below that consider the specification types WORKORDER and PRODUCTION_VERSION. It should be noted that a customized implementation tailored to the respective requirements may differ.

1. The initial creation of VART-class

The initial creation of a specification class (P)

2. Implementation Interface /SCT/QP_IF_PP and master data method GET_PP_MASTER_DATA

This method is used to build the bill of materials and routing data of the specification

Usage /SCT/QP_IF

PUBLIC SECTION.
INTERFACES /sct/qp_if_pp .
 
METHOD /sct/qp_if_pp~get_pp_master_data.
* Call of PP-service-method. Converting specification structur into structture MKAL, STPO and PLPO
rt_dispo = mo_pp_service->/sct/qp_if_pp_service~map_instance_to_masterdata(
is_origin = is_origin
io_vart = me ).
ENDMETHOD.

This method is needed for both specification types WORKORDER and PRODUCTION_VERSION.

3. Implementation of SYNC methods SYNC_ORDER_TO_QPPD and SYNC_QPPD_TO_ORDER

The following two methods must be implemented for the WORKORDER specification type via the /SCT/QP/IF/PP interface. These are required to create the synchronization between the QPPD object for the production order and the standard SAP.

METHOD /sct/qp_if_pp~sync_order_to_qppd.
TRY.
mo_pp_service->workorder_to_qppd(
EXPORTING
ir_header = ir_header
ir_operations = ir_operations
ir_bom = ir_bom
io_vart = me
iv_syncmodel = c_mapping_workorder
).
CATCH /sct/qp_cx_error INTO DATA(lo_error).
ENDTRY.
ENDMETHOD.
 
METHOD /sct/qp_if_pp~sync_qppd_to_order.
TRY.
mo_pp_service->qppd_to_workorder(
EXPORTING
ir_header = ir_header
ir_operations = ir_operations
ir_bom = ir_bom
io_vart = me
iv_syncmodel = c_mapping_workorder
CATCH /sct/qp_cx_error INTO DATA(lo_error).
ENDTRY.
ENDMETHOD.

4. Builder: generation of nodes

The specification type PRODUCTION_VERSION has a structure with recursive assemblies: 

image-20240606-091657.png

The Builder methods build this structure. The SAP standard bills of material and routings serve as the basis.

Builder

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Protected Method /SCT/MT_CL_PRODUCTION_VERSION->BUILD
* +-------------------------------------------------------------------------------------------------+
* | [!CX!] /SCT/QP_CX_ERROR
* | [!CX!] /SCT/MT_CX_ERROR
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD build.
 
/sct/mt_if_production_version~mo_production ?=
/sct/mt_cl_production=>/sct/mt_if_production~get_instance(
iv_material = /sct/qp_if_vart~mrs_node_data->matnr
iv_plant = /sct/qp_if_vart~mrs_node_data->werks ).
 
CLEAR /sct/mt_if_production_version~mo_production.
 
IF mo_ecc IS NOT BOUND.
mo_ecc = /sct/mt_cl_ecc=>/sct/mt_if_ecc~get_instance( iv_link_guid = /sct/qp_if_vart~mrs_node_data->guid ).
IF mo_ecc IS NOT BOUND.
mo_ecc = /sct/mt_cl_ecc=>/sct/mt_if_ecc~get_instance( iv_doctype = mc->doctype_marc iv_link_guid = /sct/qp_if_vart~mrs_node_data->guid ).
ENDIF.
ENDIF.
 
IF mo_ecc IS BOUND.
/sct/mt_if_production_version~mo_production ?= mo_ecc->get_production( ).
ENDIF.
 
IF /sct/qp_if_vart~mrs_node_data->noderel IS INITIAL.
build_init( ).
RETURN.
ENDIF.
 
build_key_set( ).
 
DATA(lr_assembly) = get_base( iv_vtyp = 'ASSEMBLY' ).
 
DATA(lt_itembase) = mt_itembase.
 
LOOP AT lt_itembase REFERENCE INTO DATA(lr_itembase).
lr_itembase->val = build_key_get( it_element = lr_itembase->val ).
 
build_delete_items( ir_itembase = lr_itembase ).
 
IF is_item_existing( ir_itembase = lr_itembase ) = abap_false.
CASE lr_itembase->vtyp.
WHEN const-operation-vtyp.
build_operation( is_itembase = lr_itembase->* ir_parent = lr_assembly ).
WHEN const-testing-vtyp.
build_testing( is_itembase = lr_itembase->* ir_parent = lr_assembly ).
WHEN OTHERS.
ENDCASE.
ENDIF.
ENDLOOP.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Protected Method /SCT/MT_CL_PRODUCTION_VERSION->BUILD_INIT
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
 
METHOD build_init.
**********************************************************************
* Creation of the QPPD hierarchy based on the SAP production version
**********************************************************************
 
* determine kmat
* Use PP_SERVICE and obtain the routing and bill of materials
* build nodes on this basis
 
DATA(lv_kmat) = /sct/mt_if_production_version~mo_production->/sct/qp_if_vart~get_value( 'MT_MATNR_CONF' ).
 
IF lv_kmat IS INITIAL.
lv_kmat = 'SLAB'.
ENDIF.
 
DATA : lo_mm_service TYPE REF TO /sct/qp_if_mm_service.
 
/sct/qp_cl_factory=>get_instance( CHANGING co_instance = lo_mm_service ).
 
DATA(lr_fvers) = lo_mm_service->get_prodversion(
EXPORTING
iv_matnr = lv_kmat
iv_werks = CONV #( /sct/qp_if_vart~get_value( 'MT_PLANT' ) )
iv_verid = CONV #( /sct/qp_if_vart~get_value( 'MT_VERSION' ) )
).
do_map_from_masterdata( lr_fvers ).
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Protected Method /SCT/MT_CL_PRODUCTION_VERSION->BUILD_OPERATION
* +-------------------------------------------------------------------------------------------------+
* | [--->] IS_ITEMBASE TYPE TY_ITEMBASE
* | [--->] IR_PARENT TYPE REF TO /SCT/QP_S_BASE
* | [<-()] RS_ITEMBASE TYPE REF TO /SCT/QP_S_BASE
* | [!CX!] /SCT/QP_CX_ERROR
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD build_operation.
 
DATA(ls_node) = CORRESPONDING /sct/qp_s_qvc_data_sort( is_itembase ).
ls_node-vname = 'OPERATION'.
build_create_item( is_base_parent = ir_parent->*
iv_posnr = CONV #( 9999 - is_itembase-posnr )
is_node_target = ls_node ).
ENDMETHOD.
 
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Protected Method /SCT/MT_CL_PRODUCTION_VERSION->DO_MAP_FROM_MASTERDATA
* +-------------------------------------------------------------------------------------------------+
* | [--->] IR_PRODVERS TYPE REF TO /SCT/QP_IF_MM_SERVICE=>TY_PRODVERSION
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD do_map_from_masterdata.
**********************************************************************
*Creates a QPPD hierarchy based on the selected production version
**********************************************************************
 
DATA(ls_node) = VALUE /sct/qp_s_qvc_data_sort( vtyp = const-assembly-vtyp
hiera = const-assembly-hiera
ovart = const-vart
vname = 'Assembly'
matnr = mrs_node_data->matnr
werks = mrs_node_data->werks
).
 
DATA(lr_assembly) = build_create_item( is_base_parent = ms_base
is_node_target = ls_node ).
 
DATA(lt_stpo) = ir_prodvers->t_stpo->
DATA(lt_plpo) = ir_prodvers->t_plpo->
SORT lt_plpo BY vornr DESCENDING.
 
DATA(lv_first) = lines( lt_plpo ).
 
LOOP AT lt_plpo REFERENCE INTO DATA(lr_plpo).
 
DATA(lv_line) = sy-tabix.
do_map_from_plpo( EXPORTING ir_plpo = lr_plpo
ir_parent = lr_assembly
ir_prodvers = ir_prodvers
iv_first_op = xsdbool( lv_line = lv_first )
CHANGING ct_stpo = lt_stpo ).
ENDLOOP.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Protected Method /SCT/MT_CL_PRODUCTION_VERSION->DO_MAP_FROM_PLPO
* +-------------------------------------------------------------------------------------------------+
* | [--->] IR_PLPO TYPE REF TO /SCT/QP_IF_MM_SERVICE=>TY_PLPO
* | [--->] IR_PRODVERS TYPE REF TO /SCT/QP_IF_MM_SERVICE=>TY_PRODVERSION(optional)
* | [--->] IV_FIRST_OP TYPE BOOLEAN
* | [--->] IR_PARENT TYPE REF TO /SCT/QP_S_BASE
* | [<-->] CT_STPO TYPE /SCT/QP_IF_MM_SERVICE=>TTY_STPO
* +--------------------------------------------------------------------------------------</SIGNATURE>
 
METHOD do_map_from_plpo.
**********************************************************************
* Creates a QPPD node matching the current operation
* and assigns the appropriate BOM component
**********************************************************************
 
FIELD-SYMBOLS: <field> TYPE any,
<value> TYPE any.
 
READ TABLE mt_itembase INTO DATA(ls_data)
WITH KEY vtyp = const-operation-vtyp.
CHECK sy-subrc = 0.
DATA : ls_val LIKE LINE OF ls_data-val.
 
DATA(lt_comp) = mo_tools->get_struct_comp( io_data = ir_plpo ).
 
LOOP AT lt_comp ASSIGNING FIELD-SYMBOL(<comp>).
UNASSIGN <field>.
ASSIGN COMPONENT <comp>-name OF STRUCTURE ir_plpo->* TO <field>.
CHECK <field> IS ASSIGNED AND <field> IS NOT INITIAL.
 
IF <comp>-name = 'VORNR'.
ls_data-posnr = CONV #( <field> ).
ENDIF.
mo_pp_service->map_tablefield_to_element(
EXPORTING
iv_syncmodel = c_mapping_prodversion
iv_tabname = mo_pp_service->c_table-masterdata-operation
iv_fieldname = CONV #( <comp>-name )
IMPORTING
es_map = DATA(ls_map)
).
 
IF ls_map-element IS NOT INITIAL.
CLEAR ls_val.
ls_val-element = ls_map-element.
IF ls_map-objekttyp IS NOT INITIAL.
ls_val-objekttyp = ls_map-objekttyp.
ELSE.
ls_val-objekttyp = 'OPER_ACTIVITY'.
ENDIF.
 
READ TABLE ls_data-val ASSIGNING FIELD-SYMBOL(<val>)
WITH KEY element = ls_val-element
objekttyp = ls_val-objekttyp.
IF sy-subrc NE 0.
INSERT ls_val INTO TABLE ls_data-val ASSIGNING <val>.
ENDIF.
IF <val> IS ASSIGNED.
UNASSIGN <value>.
ASSIGN COMPONENT ls_map-valfield OF STRUCTURE <val> TO <value>.
IF <value> IS ASSIGNED.
<value> = CONV #( <field> ).
ENDIF.
ENDIF.
ENDIF.
ENDLOOP.
 
LOOP AT ir_prodvers->t_plmz->* REFERENCE INTO DATA(lr_plmz)
WHERE plnty = ir_prodvers->plko->plnty
AND plnnr = ir_prodvers->plko->plnnr
AND plnal = ir_prodvers->plko->plnal
AND stlty = ir_prodvers->cstmat->stlty
AND stlnr = ir_prodvers->cstmat->stlnr
AND stlal = ir_prodvers->cstmat->stlal.
 
READ TABLE ct_stpo REFERENCE INTO DATA(lr_stpo)
WITH KEY stlkn = lr_plmz->stlkn.
IF sy-subrc = 0.
DATA(lv_index) = sy-tabix.
do_map_from_stpo( EXPORTING ir_stpo = lr_stpo CHANGING cs_data = ls_data ).
DELETE ct_stpo INDEX lv_index.
ENDIF.
ENDLOOP.
 
IF iv_first_op = abap_true.
 
LOOP AT ct_stpo REFERENCE INTO lr_stpo.
do_map_from_stpo( EXPORTING ir_stpo = lr_stpo CHANGING cs_data = ls_data ).
ENDLOOP.
ENDIF.
 
mo_cust->get_node_hiera_elements(
EXPORTING
iv_vart = mrs_node_data->vart
iv_vtyp = ls_data-vtyp
iv_hiera = ls_data-hiera
IMPORTING
et_element_usage = DATA(lt_usage)
 
DATA(lt_element) = VALUE /sct/qp_th_element( FOR GROUPS OF <wa> IN lt_usage GROUP BY <wa>-element ( <wa>-element ) ).
 
ls_data-val = FILTER #( ls_data-val IN lt_element WHERE element = table_line ).
 
DATA(lr_op) = build_operation( is_itembase = ls_data ir_parent = ir_parent ).
 
ENDMETHOD.
 
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Protected Method /SCT/MT_CL_PRODUCTION_VERSION->DO_MAP_FROM_STPO
* +-------------------------------------------------------------------------------------------------+
* | [--->] IR_STPO TYPE REF TO /SCT/QP_IF_MM_SERVICE=>TY_STPO
* | [<-->] CS_DATA TYPE TY_ITEMBASE
* +--------------------------------------------------------------------------------------</SIGNATURE>
 
METHOD do_map_from_stpo.
**********************************************************************
* Expands QPPD data for the operation with the appropriate
* BOM component
**********************************************************************
 
FIELD-SYMBOLS: <field> TYPE any,
<value> TYPE any.
DATA : ls_val LIKE LINE OF cs_data-val.
 
DATA(lt_comp) = mo_tools->get_struct_comp( io_data = ir_stpo ).
 
DATA(lv_rowgrp) = CONV /sct/qp_rowgrp( ir_stpo->posnr ).
 
LOOP AT lt_comp ASSIGNING FIELD-SYMBOL(<comp>).
UNASSIGN <field>.
ASSIGN COMPONENT <comp>-name OF STRUCTURE ir_stpo->* TO <field>.
CHECK <field> IS ASSIGNED AND <field> IS NOT INITIAL.
 
mo_pp_service->map_tablefield_to_element(
EXPORTING
iv_syncmodel = c_mapping_prodversion
iv_tabname = mo_pp_service->c_table-masterdata-component
iv_fieldname = CONV #( <comp>-name )
IMPORTING
es_map = DATA(ls_map)
).
 
IF ls_map-element IS NOT INITIAL.
CLEAR ls_val.
ls_val-element = ls_map-element.
IF ls_map-objekttyp IS NOT INITIAL.
ls_val-objekttyp = ls_map-objekttyp.
ELSE.
ls_val-objekttyp = 'COMPONENTS'.
ENDIF.
ls_val-rowgrp = lv_rowgrp.
 
READ TABLE cs_data-val ASSIGNING FIELD-SYMBOL(<val>)
WITH KEY element = ls_val-element
objekttyp = ls_val-objekttyp
rowgrp = ls_val-rowgrp.
IF sy-subrc NE 0.
INSERT ls_val INTO TABLE cs_data-val ASSIGNING <val>.
<val>-sortnr = ir_stpo->posnr. "Sortierung
ENDIF.
IF <val> IS ASSIGNED.
UNASSIGN <value>.
ASSIGN COMPONENT ls_map-valfield OF STRUCTURE <val> TO <value>.
IF <value> IS ASSIGNED.
<value> = CONV #( <field> ).
CASE <comp>-type->get_data_type_kind( <field> ).
WHEN cl_abap_typedescr=>typekind_float
OR cl_abap_typedescr=>typekind_packed
OR cl_abap_typedescr=>typekind_int
OR cl_abap_typedescr=>typekind_int2
OR cl_abap_typedescr=>typekind_int8.
IF <field> < 0.
SHIFT <value> LEFT DELETING LEADING space.
DATA(length) = strlen( <value> ) - 1.
<value> = '-' && <value>(length).
ENDIF.
ENDCASE.
ENDIF.
ENDIF.
ENDIF.
ENDLOOP.
ENDMETHOD.

Individual determination of the generation sequence

To enable an individual generation sequence, global generation is activated, and "Individual (VART class)" is selected in the "Assignment of object types" screen of the specification type customizing:

image-20240606-091708.png

With these settings /SCT/QP_IF_VART~SET_GENER_FLAG will be called.

SET_GENER_FLAG

PUBLIC SECTION.
METHODS /sct/qp_if_vart~set_gener_flag REDEFINITION .
 
METHOD /sct/qp_if_vart~set_gener_flag.
CASE ir_source_base->vtyp.
*---------------- ASSEMBLY ---------------------------------------------
WHEN 'ASSEMBLY'.
IF io_source_object->ms_node_object-objekttyp = 'TECHDATA'.
 
*Mark all operations linked to the assembly
rtr_base = /sct/qp_if_vart~get_base_tab( iv_vtyp = 'OPERATION'
is_hint = VALUE #( base = ir_source_base children = abap_true ) ).
ENDIF.
 
*---------------- OPERATION ---------------------------------------------
WHEN 'OPERATION'.
DATA(lr_assembly) = zif_qp_prodweg~get_parent( ir_source_base ).
 
IF io_source_object->ms_node_object-objekttyp = 'COMPONENT'.
rtr_base = VALUE #( ( lr_assembly ) ).
ENDIF.
 
"if it is the last operation mark den next assembly
DATA(lr_last_operation) = /sct/qp_if_vart~get_base( is_hint = value #( ir_base = ir_source position = 'LAST' ).
IF lr_last_operatioon = ir_source_base.
rtr_base = VALUE #( BASE rtr_base ( /sct/qp_if_vart~get_base( is_hint = value #( ir_base = ir_source vtypposition = 'LAST' ). ) ).
ENDIF.
"Gerierungsreiehnfolge setzen. Beim erneuten Ändern besteht die bereits vom Builder generierte Sortierreihenfolge nicht mehr.
ENDCASE.
 
* Sorting for generation beginning at the end
LOOP AT rtr_base INTO DATA(rs_base).
rs_base->gensort = c_max_gensort - rs_base->posnr.
ENDLOOP.
ENDMETHOD.