As you can see in the picture we have a SAP PO – Generic IDoc Outbound Interface with multiple IDoc Operation in our iFlow.
Introduction
Often you have the requirement to create a set of IDoc interfaces as a kind of template implementation, especially for EDI communication scenarios.
Regarding the EDI use case, you might have created a single iFlow receiving the partner’s data and separate the incoming messages over operations.
For the other direction you realize the IDoc sender adapter requires a dedicated IDoc outbound interface with only one operation. So we cannot provide a single template interface with multiple operations for sending data to the partner, which is resolved here SAP PI B2B Add-on 3.0 – Outbound by using EDI separator. I want to engage the sending system in order to build the SAP PO – Generic IDoc Outbound Interface…
My workaround is inspired by the blog post „Michal’s PO tips: How to send messages directly to AEX (ICO) – adapter independent – SOAPUI version“ to implement the BAdI IDOC_XML_ENVELOPE_OU in order to wrap the XML IDoc into the SOAP envelope and adding the XI headers.
If you are also searching for a solution to either…
- provide generic IDoc outbound interfaces
- create acknowledgements per partner profile and not per IDoc sender channel (additional adapter module required for ALEAUDs)
- setup a simple solution to create the serialization context for IDocs (based on Xpath)
-> Configuration instead of function module development - have a generic and single HTTP endpoint for XML-HTTP IDoc ports instead of creating multiples (one endpoint for each SAP PO outbound interface)
- avoid header mappings for outbound interfaces in order to set the XI Party (KB: 1941832 – Resolve logical receiver party) or replace the virtual receiver Logical System (KB: 2728276 – Resolve logical receiver service for LS receiver)
- send the IDoc to a dedicated iFlow having a virtual receiver, which could e.g. represent a dedicated SAP process. This helps to separate IDoc iFlows into business responsibilities and you could create IDoc iFlows without any dependency to any other IDoc interface.
(I know, it increases the amount of point2point interfaces, but if you ask business, I’m not sure they are interested in using as many integration patterns as possible 🙂
-> Less discussion about impact and regression testing after changing existing interfaces - use HTTP based communication instead of TRFC / ressource adapter configuration for sending IDocs, but still want the full/better SAP PO functionality (see restrictions) compared to the restricted default XML-HTTP port behavior
or - overcome the 16 character serialization context length
…this BAdI implementation and some little customizing might help you to achieve it with only a single iFlow.
BTW: No metadata is required anymore if you send it over HTTP-XML…
Please bear with me in case you find issues regarding the ABAP part,I’m still on beginner level.
I’m happy receive your ideas/use cases and feel free to provide any optimization proposals.
Warning: This BAdI is not yet used in production. So adjust it to your needs and test it carefully before using it in productive environments – No warranty for correctness, performance and no support can be provided! This is no SAP code. So it is 100% in your responsibility to troubleshoot and correct it in case of any issue.
Restrictions
- You will not successfully receive back ALEAUDs without an additional adapter module.
ALEAUDs will stuck in SAP PO, not finding the corresponding inbound IDoc.
I’m checking if I can publish the module. - Your WE21 port name must match *PO[X]_* regex: ^.*PO.{0,1}_.*$ according to the code below.
Steps to implement the BAdI
Before building the SAP PO – Generic IDoc Outbound Interface, please check the perquisites.
Prerequisites
You should have at least basic ABAP development knowledge and create a package or know which development package needs to be selected before you continue with the next steps.
You must have at least 7.40 SAP Basis release to use the BAdI code below.
1. SAP PO SOAP sender channel (in SAP PO)
Make sure there is an active SOAP sender channel created for the Business System in where you implement the BAdI and this channel is used in ANY deployed iFlow/ICO, like shown in the referenced blog entry from above.
2. Create RFC Destination (SM59)
Type: G
Host: your SAP PO system (without protocol, like http[s]://)
Service Your SAP PO Http Port
Path Prefix: /XISOAPAdapter/MessageServlet?channel=:<SenderComponentName>:<ChannelName>
Setup Authentication
e.g. /XISOAPAdapter/MessageServlet?channel=:ER6_800:CC_SND_SOAP
Please note the colons which separate Party (empty in our case) from the sender system (business system)
3. Create HTTP Idoc Port (WE21)
Name: SAP_PO_XI and assign it in the partner profile (copy/write the value manually if you do not find it with F4 help)
4. Create domains (SE11)
(Select domain, enter name and click create button for all 2)
Name: ZSSTRING
Description: <something you like>
Data Type: SSTRING
No. Characters: 1133
Lower Case: X
Name: ZRCVPRN
Description: <something you like>
Data Type: CHAR
No. Characters: 60
Lower Case: X
-> Save and activate both new domains
5. Create data elements (SE11)
(Select datatype, enter name and click create button, choose data element for all 2)
Name: ZXPATH
Description: <something you like>
Domain: ZSSTRING
Field Label Tab
Short: 10 <something you like>
Name: ZRCVPRN
Description: <something you like>
Domain: ZRCVPRN
Field Label Tab
Short: 10 <something you like>
-> Save and activate all 2 new data types
6. Create tables (SE11)
(Select database table, enter name and click create button for all 2)
Name: ZIDOCENVELOPEOUT
Delivery and Maintenance Tab
Delivery Class: C
Data Browser/Table Maintenance: Display Maintenance Allowed
Fields Tab
Field | Data Element | Key |
.INCLUDE | EDK13 | X |
IDOCTP | EDIPIDOCTP | |
CIMTYP | EDIPCIMTYP | |
QIDXPATH | ZXPATH | |
CUSTSENDIF | PRX_INTFID | |
CUSTSENDNS | PRX_NSPCE | |
ERRORACK | SAP_BOOL | |
SYSTEMACK | SAP_BOOL |
-> Technical settings
Data Class: APPL0
Size Category: 0
Regarding Buffering I am not sure, I’ve chosen “Not Allowed”
Log data changes X (Check with Basis for profile parameter rec/client <client> to make use of it)+
-> Extras -> Enhancement category -> can be enhanced
Name: ZIDOCENVELOUTNRO
Delivery and Maintenance Tab
Delivery Class: A
Data Browser/Table Maintenance: Display Maintenance Allowed with Restrictions
Fields Tab
Field | Data Element | Key |
.INCLUDE | EDK13 | X |
IDOCTP | EDIPDOCTYP | X |
CIMTYP | EDIPCIMTYP | X |
QUEUEID | CHAR16 | X |
ZRCVPRN | ZRCVPRN | X |
CHANGEDATE | AS4DATE | |
CHANGETIME | AS4TIME | |
XPATHRESULT | ZXPATH | |
QIDXPATH_V | ZXPATH |
-> Technical settings
Data Class: APPL1
Size Category: 2
Regarding Buffering I am not sure, I’ve chosen “Not Allowed”
Log data changes [ ] No maintenance required -> no logging.
-> Extras -> Enhancement category -> can be enhanced
-> Save and activate both new database tables | ignore warnings about key length
7. Create number range object (SNRO)
(Select datatype, enter name and click create button, choose data element)
Name: ZQUEUEID
ShortTxt: <something you like>
Number length domain: char16
Buffering: No (if you don’t like gaps)
Interval name: Z1
From: 0000000000000001
To: 9999999999999999
8. Create the BAdI class (SE18)
(Select BAdI Name, enter IDOC_XML_ENVELOPE_OU and click display button)
Transaction-Code: SE18
BAdI Name: IDOC_XML_ENVELOPE_OU
-> Implementation -> Create
Name: ZIDOC_XML_ENVELOPE_O
(enter name and click OK (green arrow) button)
Tab Interface
Double-click on PROCESS
(Click on Source code Based)
Copy and insert the code.
Save and activate it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 | CLASS zcl_im_idoc_xml_envelope_o DEFINITION PUBLIC FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES if_ex_idoc_xml_envelope_ou . PROTECTED SECTION. PRIVATE SECTION. CONSTANTS gc_idoc_xml_envelope_ou_name TYPE string VALUE 'ZCLIMIDOCXMLENV_O' ##NO_TEXT. CONSTANTS gc_idoc_xml_envelope_ou_rcvpor TYPE string VALUE '^.*PO.{0,1}_.*$' ##NO_TEXT. CONSTANTS gc_idoc_xml_zidocenvelpout_v TYPE string VALUE 'ZIDOCENVELOPEOUT' ##NO_TEXT. CONSTANTS gc_direct_outbound TYPE string VALUE '1' ##NO_TEXT. METHODS create_message IMPORTING !iv_msgno TYPE edi_stamno !iv_msgv1 TYPE any OPTIONAL !iv_msgv2 TYPE any OPTIONAL !iv_msgv3 TYPE any OPTIONAL !iv_msgv4 TYPE any OPTIONAL RETURNING VALUE(rs_message) TYPE idocstatmp . METHODS evaluate_xpath IMPORTING !idoc_string TYPE string !xpath TYPE zxpath RETURNING VALUE(result) TYPE string . METHODS getqueueid IMPORTING !control TYPE edidc !xpath TYPE zxpath !xpathresult TYPE zxpath !zrcvprn TYPE zrcvprn RETURNING VALUE(queueid) TYPE char16 . METHODS findenveloperecord IMPORTING !control TYPE edidc !xpath TYPE zxpath !xpathresult TYPE zxpath !zrcvprn TYPE zrcvprn RETURNING VALUE(env_record) TYPE zidocenveloutnro . ENDCLASS. CLASS ZCL_IM_IDOC_XML_ENVELOPE_O IMPLEMENTATION. * <SIGNATURE>---------------------------------------------------------------------------------------+ * | Instance Private Method ZCL_IM_IDOC_XML_ENVELOPE_O->CREATE_MESSAGE * +-------------------------------------------------------------------------------------------------+ * | [--->] IV_MSGNO TYPE EDI_STAMNO * | [--->] IV_MSGV1 TYPE ANY(optional) * | [--->] IV_MSGV2 TYPE ANY(optional) * | [--->] IV_MSGV3 TYPE ANY(optional) * | [--->] IV_MSGV4 TYPE ANY(optional) * | [<-()] RS_MESSAGE TYPE IDOCSTATMP * +--------------------------------------------------------------------------------------</SIGNATURE> METHOD create_message. * Check for consistency IF iv_msgno IS INITIAL. RAISE EXCEPTION TYPE cx_fatal_exception. ENDIF. CLEAR rs_message. * Fill message structure rs_message-stamid = gc_idoc_xml_envelope_ou_name. rs_message-stamno = iv_msgno. IF iv_msgv1 IS SUPPLIED. rs_message-stapa1 = iv_msgv1. ENDIF. IF iv_msgv2 IS SUPPLIED. rs_message-stapa2 = iv_msgv2. ENDIF. IF iv_msgv3 IS SUPPLIED. rs_message-stapa3 = iv_msgv3. ENDIF. IF iv_msgv4 IS SUPPLIED. rs_message-stapa4 = iv_msgv4. ENDIF. rs_message-repid = sy-repid. ENDMETHOD. * <SIGNATURE>---------------------------------------------------------------------------------------+ * | Instance Private Method ZCL_IM_IDOC_XML_ENVELOPE_O->EVALUATE_XPATH * +-------------------------------------------------------------------------------------------------+ * | [--->] IDOC_STRING TYPE STRING * | [--->] XPATH TYPE ZXPATH * | [<-()] RESULT TYPE STRING * +--------------------------------------------------------------------------------------</SIGNATURE> METHOD evaluate_xpath. DATA: separator(1) TYPE c VALUE '&', full TYPE abap_bool. * DESCRIBE FIELD result LENGTH DATA(clen) IN CHARACTER MODE. " no neeed to loop more than max length DATA(xpp) = NEW cl_proxy_xpath( ). xpp->set_source_string( idoc_string ). xpp->run( expression = xpath ). DATA(nodes) = xpp->get_nodes( ). IF NOT nodes IS INITIAL. DATA(node) = nodes->get_next( ). WHILE node IS BOUND AND NOT node IS INITIAL AND NOT full = abap_true. DATA(s) = node->get_value( ). DATA(typ) = node->get_type( ). CONCATENATE result s INTO result SEPARATED BY separator . node = nodes->get_next( ). ENDWHILE. REPLACE REGEX '^' && separator && '+|' && separator && '+$' IN result WITH ''. " remove pipe at the end if there is one ENDIF. ENDMETHOD. * <SIGNATURE>---------------------------------------------------------------------------------------+ * | Instance Private Method ZCL_IM_IDOC_XML_ENVELOPE_O->FINDENVELOPERECORD * +-------------------------------------------------------------------------------------------------+ * | [--->] CONTROL TYPE EDIDC * | [--->] XPATH TYPE ZXPATH * | [--->] XPATHRESULT TYPE ZXPATH * | [--->] ZRCVPRN TYPE ZRCVPRN * | [<-()] ENV_RECORD TYPE ZIDOCENVELOUTNRO * +--------------------------------------------------------------------------------------</SIGNATURE> METHOD findenveloperecord. SELECT * FROM zidocenveloutnro WHERE rcvprt = @control-rcvprt AND rcvprn = @control-rcvprn AND rcvpfc = @control-rcvpfc AND mestyp = @control-mestyp AND mescod = @control-mescod AND mesfct = @control-mesfct AND test = @control-test AND idoctp = @control-idoctp AND cimtyp = @control-cimtyp AND xpathresult = @xpathresult AND zrcvprn = @zrcvprn AND qidxpath_v = @xpath INTO @env_record. ENDSELECT. ENDMETHOD. * <SIGNATURE>---------------------------------------------------------------------------------------+ * | Instance Private Method ZCL_IM_IDOC_XML_ENVELOPE_O->GETQUEUEID * +-------------------------------------------------------------------------------------------------+ * | [--->] CONTROL TYPE EDIDC * | [--->] XPATH TYPE ZXPATH * | [--->] XPATHRESULT TYPE ZXPATH * | [--->] ZRCVPRN TYPE ZRCVPRN * | [<-()] QUEUEID TYPE CHAR16 * +--------------------------------------------------------------------------------------</SIGNATURE> METHOD getqueueid. DATA: enqueued TYPE abap_bool, wa_record TYPE zidocenveloutnro, rc TYPE inri-returncode, loops TYPE n VALUE 1, maxloops TYPE i VALUE 10. TRY. * Query to get the unique (single) record if same was already processed. wa_record = me->findenveloperecord( control = control xpath = xpath xpathresult = xpathresult zrcvprn = zrcvprn ). WHILE NOT enqueued EQ abap_true AND loops < maxloops AND wa_record IS INITIAL. " enqueue table for insert loops = loops + 1. " avoid endless loop * Enqueue for insert, if new CALL FUNCTION 'ENQUEUE_E_TABLE' EXPORTING mode_rstable = 'E' _wait = 'X' tabname = 'ZIDOCENVELOUTNRO'. IF sy-subrc = 0. enqueued = abap_true. ELSE. IF loops >= maxloops . RAISE EXCEPTION TYPE cx_abap_pragma_enqueue. ENDIF. WAIT UP TO 2 SECONDS. ENDIF. * Check again to make sure no insert happened during wait for enqueue wa_record = me->findenveloperecord( control = control xpath = xpath xpathresult = xpathresult zrcvprn = zrcvprn ). IF enqueued EQ abap_true AND wa_record IS INITIAL. " should only be reached if enqued IF xpathresult IS NOT INITIAL. " only if serialization is required CALL FUNCTION 'NUMBER_GET_NEXT' EXPORTING nr_range_nr = 'Z1' object = 'ZQUEUEID' IMPORTING number = queueid returncode = rc. IF sy-subrc <> 0 OR rc <> 0. RAISE EXCEPTION TYPE cx_abap_load_error. ENDIF. ENDIF. wa_record = VALUE #( BASE CORRESPONDING #( control ) qidxpath_v = xpath xpathresult = xpathresult changedate = sy-datum changetime = sy-uzeit queueid = queueid zrcvprn = zrcvprn ). INSERT INTO zidocenveloutnro VALUES wa_record. COMMIT WORK. ENDIF. *end of enque, if new ENDWHILE. * return queueid (also empty/initial ones) if successfully retrieved otherwise exception IF wa_record IS NOT INITIAL. queueid = wa_record-queueid. ELSE. RAISE EXCEPTION TYPE cx_abap_load_error. ENDIF. CATCH cx_root. RAISE EXCEPTION TYPE cx_abap_load_error. CLEANUP. IF enqueued EQ abap_true. CALL FUNCTION 'DEQUEUE_E_TABLE' EXPORTING mode_rstable = 'E' tabname = 'ZIDOCENVELOUTNRO'. IF sy-subrc <> 0. RAISE EXCEPTION TYPE cx_abap_load_error. ENDIF. ENDIF. ENDTRY. ENDMETHOD. * <SIGNATURE>---------------------------------------------------------------------------------------+ * | Instance Public Method ZCL_IM_IDOC_XML_ENVELOPE_O->IF_EX_IDOC_XML_ENVELOPE_OU~PROCESS * +-------------------------------------------------------------------------------------------------+ * | [--->] IDOC_XML TYPE XSTRING * | [--->] CONTROL TYPE EDIDC * | [<---] OUTBOUND_XML TYPE XSTRING * | [<---] ERROR TYPE CHAR1 * | [<---] PROTOCOL TYPE IDOCSTATMP * +--------------------------------------------------------------------------------------</SIGNATURE> METHOD if_ex_idoc_xml_envelope_ou~process. TYPE-POOLS: ixml. DATA: lo_stream_factory TYPE REF TO if_ixml_stream_factory, lo_input_stream TYPE REF TO if_ixml_istream, lo_output_stream TYPE REF TO if_ixml_ostream, lo_document TYPE REF TO if_ixml_document, lo_parser TYPE REF TO if_ixml_parser, lo_parse_error TYPE REF TO if_ixml_parse_error, lo_renderer TYPE REF TO if_ixml_renderer, lo_ixml TYPE REF TO if_ixml. DATA: lv_idocout_s TYPE string, errorcount TYPE integer VALUE 0. * Reset exporting parameters CLEAR outbound_xml. CLEAR error. CLEAR protocol. *+-------------------------------------------------------------------------------------------------+ * Do initial checks and return if they are not fullfilled with original payload *+-------------------------------------------------------------------------------------------------+ outbound_xml = idoc_xml. IF idoc_xml IS INITIAL. RETURN. " Exit anyway ENDIF. * Check the port to be relevant DATA: portmatch TYPE match_result. FIND FIRST OCCURRENCE OF REGEX gc_idoc_xml_envelope_ou_rcvpor IN control-rcvpor RESULTS portmatch. IF portmatch IS INITIAL. " IDOC receiver port is not a *PO[x]_ destiantion RETURN. ENDIF. IF control-direct <> gc_direct_outbound. " Direction is not "1 - Outbound" RETURN. ENDIF. TRY. * +-------------------------------------------------------------------------------------------------+ * | Convert xstring to string, default UTF-8 encoding * +-------------------------------------------------------------------------------------------------+ DATA(idoc_string) = cl_abap_codepage=>convert_from( source = idoc_xml ). * +-------------------------------------------------------------------------------------------------+ * | Save for later and now remove XML declaration from incoming XML * +-------------------------------------------------------------------------------------------------+ DATA: matches TYPE match_result, xml_declaration TYPE string. FIND FIRST OCCURRENCE OF REGEX '<\?xml.*\?>' IN idoc_string RESULTS matches. xml_declaration = idoc_string+matches-offset(matches-length). REPLACE REGEX '<\?xml.*\?>' IN idoc_string WITH ''. * +-------------------------------------------------------------------------------------------------+ * | Retrieve UUID as sysuuid_x * +-------------------------------------------------------------------------------------------------+ DATA: uuid TYPE sysuuid_x. CALL FUNCTION 'SYSTEM_UUID_CREATE' IMPORTING uuid = uuid. * +-------------------------------------------------------------------------------------------------+ * | Format Message ID with hyphens as string * +-------------------------------------------------------------------------------------------------+ DATA(msgid) = cl_soap_wsrmb_helper=>convert_uuid_raw_to_hyphened( uuid ). * +-------------------------------------------------------------------------------------------------+ * | Get UTC timestamp * +-------------------------------------------------------------------------------------------------+ DATA: lv_utc TYPE timestamp. cl_abap_tstmp=>systemtstmp_syst2utc( EXPORTING syst_date = sy-datum syst_time = sy-uzeit IMPORTING utc_tstmp = lv_utc ). errorcount = errorcount + sy-subrc. * +-------------------------------------------------------------------------------------------------+ * | Convert UTC timestamp into ISO format as string * +-------------------------------------------------------------------------------------------------+ DATA: ts TYPE string. ts = cl_xlf_date_time=>create( timestamp = lv_utc ). * +-------------------------------------------------------------------------------------------------+ * | Retrieve business system name from SLD for sender service * +-------------------------------------------------------------------------------------------------+ DATA: bs_name TYPE text60, bs_capt TYPE string, bs_role TYPE lcr_bs_role. CALL FUNCTION 'LCR_GET_OWN_BUSINESS_SYSTEM' EXPORTING bypassing_cache = ' ' read_from_cache = ' ' update_cache_entry_timestamp = ' ' IMPORTING bs_key_name = bs_name bs_caption = bs_capt bs_role = bs_role. errorcount = errorcount + sy-subrc. * +-------------------------------------------------------------------------------------------------+ * | Get data from the customizing table * +-------------------------------------------------------------------------------------------------+ SELECT SINGLE * FROM zidocenvelopeout WHERE rcvprt = @control-rcvprt AND rcvprn = @control-rcvprn AND rcvpfc = @control-rcvpfc AND mestyp = @control-mestyp AND mescod = @control-mescod AND mesfct = @control-mesfct AND idoctp = @control-idoctp AND cimtyp = @control-cimtyp INTO @DATA(custv). * +-------------------------------------------------------------------------------------------------+ * | Do the mapping of RCVPRN, which might be required for party due to SAP PO not accepting numbers * +-------------------------------------------------------------------------------------------------+ DATA: rcvprn TYPE zrcvprn. IF custv-zrcvprn IS NOT INITIAL. rcvprn = custv-zrcvprn. ELSE. rcvprn = control-rcvprn. ENDIF. * +-------------------------------------------------------------------------------------------------+ * | Run the Xpath evaluation which is required to set serialization context (QueueId) in SAP PO * +-------------------------------------------------------------------------------------------------+ DATA: xpathresult TYPE zxpath . IF custv-qidxpath IS NOT INITIAL. IF custv-qidxpath(1) = ''''. xpathresult = custv-qidxpath. REPLACE ALL OCCURRENCES OF '''' IN xpathresult WITH ''. ELSE. xpathresult = me->evaluate_xpath( idoc_string = idoc_string xpath = custv-qidxpath ). ENDIF. IF xpathresult IS INITIAL. protocol = create_message( iv_msgno = '006' iv_msgv1 = 'Xpath found no value' iv_msgv2 = 'Badi: ZCL_IM_IDOC_XML_ENVELOPE_O'). error = abap_true. RETURN. ENDIF. ENDIF. * +-------------------------------------------------------------------------------------------------+ * | Data preparation steps * +-------------------------------------------------------------------------------------------------+ DATA: systemack TYPE string, errorack TYPE string, defaultif TYPE string, queueid TYPE char16. defaultif = control-mestyp && '.' && control-idoctp && '.' && control-cimtyp. "In case no custom Idoc interface required REPLACE REGEX '\.$' IN defaultif WITH '' . " remove dot at the end in case no cimtype IF custv-systemack = abap_false. " convert to xsd:boolean systemack = 'false'. ELSE. systemack = 'true'. ENDIF. IF custv-errorack = abap_false. errorack = 'false'. ELSE. errorack = 'true'. ENDIF. " will also be called in case no queuueid is required for join of view ZIDOCENVELPOUT_V queueid = me->getqueueid( control = control xpath = custv-qidxpath xpathresult = xpathresult zrcvprn = custv-zrcvprn ). * +-------------------------------------------------------------------------------------------------+ * | Add SAP PO SOAP envelope around xml to fill xi protocol parameters dnyamically * +-------------------------------------------------------------------------------------------------+ CONCATENATE xml_declaration '<SOAP:Envelope xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/">' '<SOAP:Header>' '<sap:Main xmlns:sap="http://sap.com/xi/XI/Message/30" versionMajor="3" versionMinor="1" SOAP:mustUnderstand="1">' '<sap:MessageClass>ApplicationMessage</sap:MessageClass>' '<sap:ProcessingMode>asynchronous</sap:ProcessingMode>' '<sap:MessageId>' msgid '</sap:MessageId>' '<sap:TimeSent>' ts '</sap:TimeSent>' '<sap:Sender>' '<sap:Party agency="http://sap.com/xi/XI" scheme="XIParty"/>' '<sap:Service>' bs_name '</sap:Service>' '</sap:Sender>' '<sap:Receiver>' INTO lv_idocout_s. CASE control-rcvprt . WHEN 'LS'. CONCATENATE lv_idocout_s '<sap:Party agency="http://sap.com/xi/XI" scheme="XIParty"/>' '<sap:Service>' rcvprn '</sap:Service>' INTO lv_idocout_s . WHEN OTHERS. CONCATENATE lv_idocout_s '<sap:Party agency="http://sap.com/xi/XI" scheme="XIParty">' rcvprn '</sap:Party>' '<sap:Service></sap:Service>' INTO lv_idocout_s . ENDCASE. CONCATENATE lv_idocout_s '</sap:Receiver>' INTO lv_idocout_s . CASE custv-custsendns . WHEN ' '. CONCATENATE lv_idocout_s '<sap:Interface namespace="urn:sap-com:document:sap:idoc:messages" >' INTO lv_idocout_s . WHEN OTHERS. CONCATENATE lv_idocout_s '<sap:Interface namespace="' custv-custsendns '" >' INTO lv_idocout_s . ENDCASE. CASE custv-custsendif . WHEN ' '. CONCATENATE lv_idocout_s defaultif INTO lv_idocout_s . WHEN OTHERS. CONCATENATE lv_idocout_s custv-custsendif INTO lv_idocout_s . ENDCASE. CONCATENATE lv_idocout_s '</sap:Interface>' '</sap:Main>' '<sap:ReliableMessaging xmlns:sap="http://sap.com/xi/XI/Message/30" SOAP:mustUnderstand="1" SystemAckRequested="' systemack '" SystemErrorAckRequested="' errorack '" >' INTO lv_idocout_s . CASE custv-qidxpath . WHEN ' ' . CONCATENATE lv_idocout_s '<sap:QualityOfService>ExactlyOnce</sap:QualityOfService>' INTO lv_idocout_s . WHEN OTHERS. CONCATENATE lv_idocout_s '<sap:QualityOfService>ExactlyOnceInOrder</sap:QualityOfService>' '<sap:QueueId>' queueid '</sap:QueueId>' INTO lv_idocout_s . ENDCASE. CONCATENATE lv_idocout_s '</sap:ReliableMessaging>' '<sap:DynamicConfiguration xmlns:sap="http://sap.com/xi/XI/Message/30" SOAP:mustUnderstand="1">' '<sap:Record namespace="http://sap.com/xi/XI/System/IDoc_AAE" name="RCVPRN">' control-rcvprn '</sap:Record>' '<sap:Record namespace="http://sap.com/xi/XI/System/IDoc_AAE" name="DOCNUMS">' control-docnum '</sap:Record>' '<sap:Record namespace="http://sap.com/xi/XI/System/IDoc_AAE" name="AckData">' control-docnum ';' control-rcvprn '</sap:Record>' '</sap:DynamicConfiguration>' '<sap:HopList xmlns:sap="http://sap.com/xi/XI/Message/30" SOAP:mustUnderstand="1">' '<sap:Hop timeStamp="' ts '" wasRead="false">' '<sap:Engine type="AE">af.pot.pot</sap:Engine>' '<sap:Adapter namespace="http://sap.com/xi/XI/System">IDoc_AAE</sap:Adapter>' '<sap:MessageId>' msgid '</sap:MessageId>' '<sap:Info>localejbs/IDocAckBean</sap:Info>' '</sap:Hop>' '</sap:HopList>' '</SOAP:Header>' '<SOAP:Body>' idoc_string ' </SOAP:Body>' '</SOAP:Envelope>' INTO lv_idocout_s. * +-------------------------------------------------------------------------------------------------+ * | Prepare output and convert from string to xstring, default UTF-8 * +-------------------------------------------------------------------------------------------------+ CLEAR outbound_xml. outbound_xml = cl_abap_codepage=>convert_to( source = lv_idocout_s ). * +-------------------------------------------------------------------------------------------------+ * | Error Handling * +-------------------------------------------------------------------------------------------------+ IF errorcount <> 0. RAISE EXCEPTION TYPE cx_abap_load_error. ENDIF. CATCH cx_root INTO DATA(e_text). protocol = create_message( iv_msgno = '006' iv_msgv1 = 'ZCL_IM_IDOC_XML_ENVELOPE_O' ). error = abap_true. RETURN. ENDTRY. ENDMETHOD. ENDCLASS. |
Customizing
Use SQL for the first insert into ZIDOCENVELOPEOUT, e.g.
1 2 | insert into sapsr3.ZIDOCENVELOPEOUT (MANDT, RCVPRN, RCVPRT, RCVPFC, MESTYP, MESCOD, MESFCT, TEST) values ('your client', 'your LS', 'LS', ' ', 'your Idoc Type', ' ', ' ', ' '); commit; |
Mandatory, if you want to apply a rule, otherwise it should also work without any transformation:From now on, you can edit the table in SE16, if the client settings allow it.
Consult your Basis if you face some issues.
Please maintain IDocTp and CimTp like you have maintained it in WE20 partner profile to match outgoing IDocs.
Optional:
You can enter your Custom / Generic IDoc Interface name in column „CustSendIf“.
You can enter namespace of Custom / Generic IDoc Interface name in column „CustSendNs“.
You can enter your virtual receiver in column „zRcvPrn“:
–> in case of LI/KU/GP… the value will appear in party field, in case of LS it will appear in the service field.
You can enter your Xpath-Expression for creating the serialization context in column „qIdXpath“:
-> In case you fill it, the QoS will be EOIO automatically – make sure to enter an Xpath which returns a result.
-> In case you leave it empty the QoS will be EO.
Monitoring
You can monitor the assignment of queueids (from number range object), when they are created the first time in table ZIDOCENVELOUTNRO.
If you want, you can create a view joining ZIDOCENVELOPEOUT and ZIDOCENVELOUTNRO in order to suppress historic values (not part of this blog entry).
Outlook
Full function with an additional SAP PO Adapter Module in case persistence is switched on:
- See incoming IDocs over SAP PO – Generic IDoc Outbound Interface in IDoc monitor (KB 2345912 – IDoc Message Monitor)
- Be able to return Acknowledgements and monitor them using standard SAP PO IDoc Monitor (KB: 2992965 – IDoc ALEAUD)
Download Transportfile
Cofile: K900059.SLM
Datafile: R900059.SLM
Please consult your Basis team if you do not know how to use these files.
Feedback
Please 📧 contact us if you find any issue or have questions about it.
This post was first published on https://blogs.sap.com/