Outbound messaging > Real-time inventory quantities (ATP)
Overview: real-time inventory for materials
Current material stock quantities (usually referred to as Available-to-promise or ATP stock) is widely required e.g. to present the current and orderable number of products in an online store. But ATP is not a static value within SAP systems, unfortunately. To determine current ATP quantities, SAP systems run an on-line calculation that considers reservations, inbound pipeline, planned shipment dates etc.
Therefore, sending real-time ATP stock per material requires the detection of all changes that can affect the stock quantity calculation, an actual calculation and the formatting to make it sendable as a message, with quantity per material, units etc.
ASAPIO provides that with its ATP extraction feature, including pre-delivered example function modules that can be adopted by customers.
Required configuration
SAP Business Objects that can be used and necessary BAdI/User Exit implementation.
Object name | Object type | Configuration |
Sales Order | BUS2032 | Standard |
Purchase Order | BUS2012 | Standard |
Delivery | LIKP | Standard |
Goods Movement | BUS2017 | BAdI (MB_DOCUMENT_UPDATE) |
Material Reservation | ZBUS2093 | User Exit (MBCF0007) |
Create Outbound ATP Configuration
Create Extracting Func. Module:
- Transaction: SE37
- Create function module: Z_ACI_EXTRACTOR_ATP
Example coding:
FUNCTION z_aci_extractor_atp . *"---------------------------------------------------------------------- *"*"Local Interface: *" IMPORTING *" VALUE(IV_INSTANCE) TYPE /ASADEV/AMR_ARIBA_ARIBA_INST *" VALUE(IV_OBJECT) TYPE /ASADEV/AMR_ARIBA_ARIBA_OBJECT *" VALUE(IT_BDCP_LINES) TYPE /ASADEV/ACI_TT_BDCP *" EXPORTING *" VALUE(ET_CSV_LINES) TYPE /ASADEV/AMR_TT_CSV_LINE *" VALUE(ET_RETURN) TYPE /ASADEV/ACI_TT_BAPIRET2 *"---------------------------------------------------------------------- DATA: ls_bdcp TYPE bdcp, ls_csv_line TYPE /asadev/aci_any_string, lv_tmp_event TYPE string, lv_tmp_key TYPE string, lv_keyname TYPE /asadev/aci_head_attr_value, ls_return TYPE bapiret2. " ATP check DATA: lv_werks TYPE werks, lv_matnr TYPE matnr, lv_meins TYPE meins, lt_iwmdvsx TYPE STANDARD TABLE OF bapiwmdvs,"TABLES PARAM ls_iwmdvsx LIKE LINE OF lt_iwmdvsx , lt_iwmdvex TYPE STANDARD TABLE OF bapiwmdve,"TABLES PARAM ls_iwmdvex LIKE LINE OF lt_iwmdvex. DATA: lv_bdcnt TYPE string, lv_req_date TYPE string, lv_req_qty TYPE string, lv_com_date TYPE string, lv_com_qty TYPE string, lv_av_qty_plt TYPE bapicm61v-wkbst, lv_av_qty_plt_str TYPE string, lv_lines TYPE i, ls_return_bapi TYPE bapireturn. DATA: lb_last_row TYPE abap_bool VALUE abap_false. "populate fields of struture and append to itab APPEND ls_iwmdvsx TO lt_iwmdvsx. "populate fields of struture and append to itab APPEND ls_iwmdvex TO lt_iwmdvex. CONSTANTS: lc_auth_obj_rc TYPE xuobject VALUE '/ASADEV/RC', "Mod-001++ lc_auth_fld_rf TYPE fieldname VALUE '/ASADEV/RF', "Mod-001++ lc_message_id TYPE symsgid VALUE '/ASADEV/AMR_MESSAGE', "Mod-001++ lc_message_number TYPE symsgno VALUE '077', "Mod-001++ lc_autch_check_data_proc TYPE /asadev/aci_rfc_auth_check VALUE 'DATA_PROC'."Data processing "Mod-001++ * Check if user can execute RFC AUTHORITY-CHECK OBJECT lc_auth_obj_rc ID lc_auth_fld_rf FIELD lc_autch_check_data_proc. IF sy-subrc <> 0. * No authorization for user &1 to call ACI function! ls_return-type = /asadev/cl_aci_helper=>mc_aci_error. ls_return-id = lc_message_id. ls_return-number = lc_message_number. ls_return-message_v1 = sy-uname. APPEND ls_return TO et_return. CLEAR ls_return. RETURN. ENDIF. /asadev/cl_aci_helper=>get_header_attribute( EXPORTING iv_instance = iv_instance iv_object = iv_object iv_attribute = /asadev/cl_aci_cloudev_helper=>mc_attr_em_key_name RECEIVING rv_value = lv_keyname ). DESCRIBE TABLE it_bdcp_lines LINES lv_lines. LOOP AT it_bdcp_lines INTO ls_bdcp. "lv_tmp_event = ls_bdcp-tabkey(32). "lv_tmp_key = ls_bdcp-tabkey+35(70). "ATP lv_werks = ls_bdcp-tabkey+21(4). lv_matnr = ls_bdcp-tabkey(18). lv_meins = ls_bdcp-tabkey+18(3). IF lv_lines = sy-tabix. lb_last_row = abap_true. ENDIF. *********************************************************** CALL FUNCTION 'BAPI_MATERIAL_AVAILABILITY' EXPORTING plant = lv_werks " Plant material = lv_matnr " Material number unit = lv_meins " Unit of measure for display * check_rule = " Checking rule * stge_loc = " Storage location * batch = " Batch * customer = " Customer number * doc_number = " Document number * itm_number = " Item number * wbs_elem = " WBS Element * stock_ind = " Special Stock Indicator * dec_for_rounding = " No. of decimal places to which rounding should be performed * dec_for_rounding_x = " Updated information in related user data field * read_atp_lock = " Control indicator for availability check * read_atp_lock_x = " Updated information in related user data field * material_evg = " Updated information in related user data field IMPORTING * endleadtme = " End of replenishment lead time av_qty_plt = lv_av_qty_plt " Quantity available at plant level * dialogflag = " Indicator (X = not available, N = no check) return = ls_return_bapi " Indicator (X = not available, N = no check) TABLES wmdvsx = lt_iwmdvsx " Input table (date and quantity) wmdvex = lt_iwmdvex. " Output table (date and ATP quantity) IF sy-subrc EQ 0. lv_av_qty_plt_str = lv_av_qty_plt. CONCATENATE ls_csv_line '{' '"objectType":"' ls_bdcp-tabname '", ' * cl_abap_char_utilities=>newline ' "event":"' lv_tmp_event '", ' cl_abap_char_utilities=>newline ' "werks":"' lv_werks '", ' cl_abap_char_utilities=>newline ' "matnr":"' lv_matnr '", ' cl_abap_char_utilities=>newline ' "meins":"' lv_meins '", ' cl_abap_char_utilities=>newline ' "date":"' sy-datum '", ' cl_abap_char_utilities=>newline ' "quantity":"' lv_av_qty_plt_str '"}' INTO ls_csv_line. AT FIRST. CONCATENATE '{"records":[{"value":[' ls_csv_line INTO ls_csv_line. ENDAT. IF lb_last_row = abap_false. CONCATENATE ls_csv_line ',' INTO ls_csv_line. ENDIF. AT LAST. CONCATENATE ls_csv_line ']}]}' INTO ls_csv_line. ENDAT. APPEND ls_csv_line TO et_csv_lines. CLEAR ls_csv_line. ENDIF. ENDLOOP. ENDFUNCTION.
Create an outbound object configuration:
- Transaction: SPRO
- Go to IMG > ASAPIO Cloud Integrator – Connection and Replication Object Customizing
- Or go directly to transaction: /ASADEV/68000202
- Select the created Connection
- Go to section Outbound Objects
- Add New Entry and specify:
-
- Object: name of the outbound configuration
- Extraction Func. Module: Z_ACI_EXTRACTOR_ATP
- Load Type: Incremental Load
- Trace: activate for testing purposes
-
Set-up Business Object Event Linkage
Create Receiver Func. Module:
- Transaction: SE37
- Create function module: Z_ACI_EVENTS_TRIGGER_ATP
Example coding:
FUNCTION Z_ACI_EVENTS_TRIGGER_ATP. *"-------------------------------------------------------------------- *"*"Local Interface: *" IMPORTING *" VALUE(SENDER) TYPE SIBFLPORB *" VALUE(EVENT) TYPE SIBFEVENT *" VALUE(RECTYPE) TYPE SWFERECTYP *" VALUE(HANDLER) TYPE SIBFLPORB *" VALUE(EXCEPTIONS_ALLOWED) TYPE SWEFLAGS-EXC_OK OPTIONAL *" VALUE(XML_SIZE) TYPE SWF_XMLSIZ *" VALUE(EVENT_CONTAINER) TYPE SWF_XMLCNT *" EXPORTING *" VALUE(RESULT) TYPE SWFREVRSLT *"-------------------------------------------------------------------- * Data declaration * Definition of local constants CONSTANTS: lc_key TYPE sychar03 VALUE 'KEY', lc_i TYPE cdchngind VALUE 'I', lc_bor_object TYPE sibfcatid VALUE 'BO', lc_auth_obj_rc TYPE xuobject VALUE '/ASADEV/RC', lc_auth_fld_rf TYPE fieldname VALUE '/ASADEV/RF', lc_autch_check_framework TYPE /asadev/aci_rfc_auth_check VALUE 'FRAMEWORK'. * Definition of local values DATA: lv_bor_type TYPE sibftypeid, lv_msgtype TYPE edi_mestyp, lv_cpident TYPE cdchangenr, lv_instance TYPE /asadev/amr_ariba_ariba_inst, lv_object TYPE /asadev/amr_ariba_ariba_object, lv_qname TYPE trfcqnam. DATA: ls_aci_syn TYPE /asadev/aci_sync, ls_amr_obj TYPE /asadev/amr_obj, ls_bdcp_lines TYPE bdcp. DATA: lv_meins TYPE meins, lv_werks TYPE werks, lv_matnr TYPE matnr, lv_len TYPE i, lv_object_type TYPE string, lv_mblnr TYPE mblnr, lv_vbeln TYPE vbeln, lv_ebeln TYPE ebeln, lv_rsnum TYPE rsnum. * Definition of internal tables DATA: lt_bdcp_lines TYPE /asadev/aci_tt_bdcp, lt_marc TYPE STANDARD TABLE OF marc, lt_vbap TYPE STANDARD TABLE OF vbap, lt_ekpo TYPE STANDARD TABLE OF ekpo, lt_mseg TYPE STANDARD TABLE OF mseg, lt_resb TYPE STANDARD TABLE OF resb, lt_lips TYPE STANDARD TABLE OF lips. DATA: l_event_container_handle TYPE REF TO if_swf_cnt_container, lr_container TYPE REF TO cl_swf_cnt_container. FIELD-SYMBOLS: TYPE marc, TYPE vbap, TYPE ekpo, TYPE mseg, TYPE resb, TYPE lips. lv_msgtype = rectype. " now check which outbound object to use SELECT SINGLE * FROM /asadev/amr_obj INTO ls_amr_obj WHERE msgtype EQ lv_msgtype. * Check if user can execute RFC AUTHORITY-CHECK OBJECT lc_auth_obj_rc ID lc_auth_fld_rf FIELD lc_autch_check_framework. IF sy-subrc <> 0. * Authorization check has failed. * To get a monitor and log entry we still call the framework with BDCP lines etc. * That way a missing authorization error will be shown in the ACI monitor transaction CALL FUNCTION '/ASADEV/ACI_RFC' EXPORTING iv_conn = ls_amr_obj-instance iv_object = ls_amr_obj-object. ELSE. "Authorization to continue * Refresh cp data CLEAR: lv_bor_type. lv_bor_type = sender-typeid. IF sender-catid EQ lc_bor_object. " we can use the BOR type as tabname as it will be only 10 characters long ls_bdcp_lines-tabname = lv_bor_type. ELSE. " future option?: get the tabname from a customizing table? based on rectype? ENDIF. " get the changenr from the event container TRY. CALL METHOD cl_swf_cnt_factory=>create_from_xml EXPORTING im_xml_table = event_container im_xml_size = xml_size RECEIVING re_instance = l_event_container_handle. lr_container ?= l_event_container_handle. lr_container->if_swf_cnt_element_access_1~element_get_value( EXPORTING name = 'CPIDENT' " Unique Name of Element IMPORTING value = lv_cpident " Element Value ). CATCH cx_swf_cnt_elem_not_found cx_swf_cnt_elem_type_conflict cx_swf_cnt_unit_type_conflict cx_swf_cnt_container cx_swf_utl_no_instance_found cx_swf_utl_obj_create_failed. CLEAR lv_cpident. ENDTRY. ls_bdcp_lines-fldname = lc_key. ls_bdcp_lines-cdchgid = lc_i. "Subobjects? Last 7 characters from sender-typeid e.g. zzBUS1001 IF strlen( sender-typeid ) GT 7. lv_len = strlen( sender-typeid ) - 7. lv_object_type = sender-typeid+lv_len(7). ELSE. lv_object_type = sender-typeid. ENDIF. " Select material number, plant and units of messure. CASE lv_object_type. WHEN 'BUS1001'. " Material lv_matnr = sender-instid. SELECT werks INTO CORRESPONDING FIELDS OF TABLE lt_marc FROM marc WHERE matnr = lv_matnr. SELECT SINGLE meins INTO lv_meins FROM mara WHERE matnr = lv_matnr. LOOP AT lt_marc ASSIGNING . CONCATENATE lv_matnr lv_meins -werks INTO ls_bdcp_lines-tabkey RESPECTING BLANKS. APPEND ls_bdcp_lines TO lt_bdcp_lines. " CLEAR ls_bdcp_lines. ENDLOOP. WHEN 'BUS2012'. " Purchase Order lv_ebeln = sender-instid. SELECT matnr meins werks INTO CORRESPONDING FIELDS OF TABLE lt_ekpo FROM ekpo WHERE ebeln = lv_ebeln. LOOP AT lt_ekpo ASSIGNING . CONCATENATE -matnr -meins -werks INTO ls_bdcp_lines-tabkey RESPECTING BLANKS. APPEND ls_bdcp_lines TO lt_bdcp_lines. ENDLOOP. WHEN 'BUS2032'. " Sales Order lv_vbeln = sender-instid. SELECT matnr meins werks INTO CORRESPONDING FIELDS OF TABLE lt_vbap FROM vbap WHERE vbeln = lv_vbeln. LOOP AT lt_vbap ASSIGNING . CONCATENATE -matnr -meins -werks INTO ls_bdcp_lines-tabkey RESPECTING BLANKS. APPEND ls_bdcp_lines TO lt_bdcp_lines. ENDLOOP. WHEN 'BUS2017'. " Goods Movement lv_mblnr = sender-instid(10). SELECT matnr werks meins FROM mseg INTO CORRESPONDING FIELDS OF TABLE lt_mseg WHERE mblnr = lv_mblnr. LOOP AT lt_mseg ASSIGNING . CONCATENATE -matnr -meins -werks INTO ls_bdcp_lines-tabkey RESPECTING BLANKS. APPEND ls_bdcp_lines TO lt_bdcp_lines. ENDLOOP. WHEN 'BUS2093'. " Material Reservation lv_rsnum = sender-instid. SELECT matnr werks meins FROM resb INTO CORRESPONDING FIELDS OF TABLE lt_resb WHERE rsnum = lv_rsnum. LOOP AT lt_resb ASSIGNING . CONCATENATE -matnr -meins -werks INTO ls_bdcp_lines-tabkey RESPECTING BLANKS. APPEND ls_bdcp_lines TO lt_bdcp_lines. ENDLOOP. WHEN 'LIKP'. " Outbound Delivery lv_vbeln = sender-instid. SELECT matnr werks meins FROM lips INTO CORRESPONDING FIELDS OF TABLE lt_lips WHERE vbeln = lv_vbeln. LOOP AT lt_lips ASSIGNING . CONCATENATE -matnr -meins -werks INTO ls_bdcp_lines-tabkey RESPECTING BLANKS. APPEND ls_bdcp_lines TO lt_bdcp_lines. ENDLOOP. ENDCASE. CLEAR ls_bdcp_lines. /asadev/cl_aci_helper=>create_change_pointers( EXPORTING iv_message_type = lv_msgtype CHANGING ct_bdcp_lines = lt_bdcp_lines ). COMMIT WORK AND WAIT. IF sy-subrc EQ 0. SELECT SINGLE * FROM /asadev/aci_sync INTO ls_aci_syn WHERE msgtype = lv_msgtype. IF sy-subrc = 0. IF ls_aci_syn-sync_enabled = 'X' AND ls_aci_syn-queue_enabled = 'X'. "Create Queue Name - first 22 chars of msg type lv_qname = lv_msgtype+0(22). "Set Queue Name CALL FUNCTION 'TRFC_SET_QUEUE_NAME' EXPORTING qname = lv_qname EXCEPTIONS invalid_queue_name = 1 OTHERS = 2. "Call FM as qRFC CALL FUNCTION '/ASADEV/ACI_RFC' IN BACKGROUND TASK EXPORTING iv_conn = ls_amr_obj-instance iv_object = ls_amr_obj-object it_bdcp_lines_ext = lt_bdcp_lines iv_http = abap_true. COMMIT WORK. ELSEIF ls_aci_syn-sync_enabled = 'X' AND ls_aci_syn-queue_enabled = ''. * Perform synchronous call CALL FUNCTION '/ASADEV/ACI_RFC' EXPORTING iv_conn = ls_amr_obj-instance iv_object = ls_amr_obj-object it_bdcp_lines_ext = lt_bdcp_lines iv_http = abap_true. ELSE. " in this case only write the change pointers and don't call the ACI framework ENDIF. ELSE. " defaults to synchronous calls CALL FUNCTION '/ASADEV/ACI_RFC' EXPORTING iv_conn = ls_amr_obj-instance iv_object = ls_amr_obj-object it_bdcp_lines_ext = lt_bdcp_lines iv_http = abap_true. ENDIF. ELSE. " no customized outbound object found - just store the change pointer for now ENDIF. ENDIF. ENDFUNCTION.
Link the Business Objects that will trigger the ATP:
- Go to section Event Linkage
- Add New Entry and specify:
- Object Category: BOR Object Type
- Object Type: The Business Object Type sending the event
- Event: Event to react to
- Receiver Function Module: Z_ACI_EVENTS_TRIGGER_ATP
- Type linkage active: tick the checkbox
Object name | Object type | Event | TABLES | Transaction |
Sales Order | BUS2032 | Created, Changed | VBAK, VBAP | VA01, VA02 |
Purchase Order | BUS2012 | Created, Changed | EKKO, EKPO | ME21N, ME22n |
Delivery | LIKP | Created, Changed | LIKP, LIPS | VL01, VL02 |
Goods Movement | BUS2017 | Created | MKPF, MSEG | MB11 |
Material Reservation | ZBUS2093 | Created, Changed | RKPF, RESB | MB21, MB22 |
Test example
With the available trigger inside the Add-on a notification of the ATP will be sent as a JSON-File.
Payload structure:
{ "value": [ { "objectType": "BUS2012", "werks": "1200", "matnr": "DPC1002", "meins": "ST", "date": "20221011", "quantity": "4216.000 " }, { "objectType": "BUS2012", "werks": "1200", "matnr": "DPC1003", "meins": "ST", "date": "20221011", "quantity": "2063.000 " }, { "objectType": "BUS2012", "werks": "1200", "matnr": "DPC1004", "meins": "ST", "date": "20221011", "quantity": "1385.000 " }, { "objectType": "BUS2012", "werks": "1200", "matnr": "DPC1005", "meins": "ST", "date": "20221011", "quantity": "4886.000 " } ] }