Use LSMW to Attach GOS Files to Documents

Jimbo's picture

https://s3.amazonaws.com/images.gearjunkie.com/uploads/2016/06/pack-goat-1.jpg|https://s3.amazonaws.com/images.gearjunkie.com/uploads/2016/06/pack-goats-3.jpg|https://s3.amazonaws.com/images.gearjunkie.com/uploads/2016/06/pack-goats-carry-gear.jpg|http://summitpackgoat.com/pict/al_joey.jpg|http://cdn.modernfarmer.com/wp-content/uploads/2013/09/pack-goat-hero.jpg|https://gohunt-assets-us-west-2.s3.amazonaws.com/media/Pack-goats_at.jpg|http://cdn.modernfarmer.com/wp-content/uploads/2013/09/packgoat-inset-2.jpg|https://www.walkopedia.net/summer2012/online-magazine/pack-goat-jam.jpg|https://packgoats.com/wp-content/uploads/2018/04/Pack-goat.jpg|https://t-state.com/wp-content/uploads/2022/05/600x400-PACK-GOATS-ON-HILLSIDE.png|https://backyardgoats.iamcountryside.com/wp-content/uploads/sites/2/2020/01/22140110_10214022219731611_2045257729_o-e1664978395552.jpg|https://www.americanhunter.org/media/0o4puu0y/packgoats_lead.jpg|https://media.rainpos.com/4209/3highline2007.jpg|https://modernfarmer.com/wp-content/uploads/2013/09/pack-goat-hero.jpgGeneric Object Services or GOS is a commonly-used technology from SAP that creates a uniform way to add functionality across various business objects in SAP. One of the functions is a feature that allows attaching a file to a business object such as a document.

As one might expect, the GOS button vanishes when the program used to create a business object is run in batch mode making it useless for a recording. Additionally, the ABAP debugger is useless as the system stops debugging as soon as the GOS button is clicked, so chasing the code rabbit down the programming hole becomes much harder.

Necessity breeds innovation

The example here was born of necessity for a client in Indiana that had a requirement to attach files that were uncompressed from the client's legacy Siebel CRM system, but not even the filenames--much less the files--were provided as source data. In order to develop the tool, some data was dummied up for testing purposes.

The first step is to define the variables used to hold the data that will be passed through the interfaces of the functions. The p_Folder parameter is hard coded with the presumption that the uncompressed files will be extracted directly to a directory on the network represented by UNC instead of being passed through a SharePoint server.

parameters:
 p_Break as checkbox,
 p_Verbos as checkbox default 'X',
 p_Folder type string default '\\Pnts044042\sap_project\SM\Work\Files'.

data:
 wa_QMEL like QMEL.

data:
 lQMNUM type SWOTOBJID-OBJKEY. " like QMEL-QMNUM.

TYPES : BEGIN OF ty_table,    "Structure for FileName
          FNAME(128) TYPE c,
        END OF ty_table.

data:
 wa_Attachement  TYPE BORIDENT, "BOR Identifier workarea
 ws_BORIDENT     TYPE BORIDENT, "BOR Identifier
 wa_SOOD4        TYPE SOOD4,
 lFolder TYPE SOODK,
 it_Files type standard table of ty_table,
 wa_Files type ty_table,
 w_h_data        TYPE SOOD2,
 w_fol_data      TYPE SOFM2,
 w_rec_data      TYPE SOOS6,
 it_Connections type standard table of BDN_CON,
 wa_Connections type BDN_CON,
 lFileDescription(128) type c, isLoaded(1) type c.

The break point is added in the BEGIN_OF_PROCESSING code block of LSMW. This launches the debugger for testing purposes and allows for break points to be added to the other code blocks during development.

if p_break eq 'X'. break-point. endif.

Next, in the BEGIN_OF_TRANSACTION, the system validates the Service Notification number and reports if it has not been created. There is little point in uploading the file to SAP before there is a document to attach it to.

If the document does exist then the BDS_GOS_CONNECTIONS_GET function is called and the returned internal table is looped through to ensure that no duplicates are created. This also enables delta loads to be performed without any additional effort.

isLoaded = ' '.
clear: wa_Files, ws_BORIDENT, wa_SOOD4, lFolder, w_h_data, w_fol_data,
       w_rec_data.
refresh: it_Files, it_Connections.
concatenate 'S' IW52S-QMNUM+2(10) into lQMNUM.
select single * from QMEL into wa_QMEL where QMNUM eq lQMNUM.
if sy-subrc ne 0.
  if p_verbos eq 'X'.
    write: / IW52S-QMNUM color col_negative, 'Not created.'.
  endif.
else.
  concatenate IW52S-ATTACHMENTNAME '.' IW52S-TYPE
   into lFileDescription.  "Use this to check if file already attached.
  call function 'BDS_GOS_CONNECTIONS_GET'
    exporting
      CLASSNAME            = 'BUS2080'
      OBJKEY               = lQMNUM
    tables
      GOS_CONNECTIONS      = it_Connections.
  loop at it_Connections into wa_Connections.
    if wa_Connections-DESCRIPT eq lFileDescription.
      isLoaded = 'X'.  "File is already attached!
      if p_verbos eq 'X'.
        write: / IW52S-QMNUM, lFileDescription(20) color col_positive,
         'file already attached.'.
      endif.
    endif.
  endloop.

Next, the program gets the folder where the file will be stored and uses that to populate the lFolder variable. It isn't clear exactly where this is or how the system stores the file, but it does really matter as most of SAP is abstracted from the underlying data storage media.

  if isLoaded ne 'X'.
    CALL FUNCTION 'SO_FOLDER_ROOT_ID_GET'
      EXPORTING
        REGION                = 'B'
      IMPORTING
        FOLDER_ID             = lFolder
      EXCEPTIONS
        OTHERS                 = 1.

With the abstracted folder location in hand, the system now attempts to read the file from the UNC on the Windows network. The SO_DOCUMENT_REPOSITORY_MANAGER function is called which, in turn, calls dozens of other esoteric functions and methods to read a file from the local PC and store it in the folder location provided.

    concatenate p_folder '\' IW52S-ATTACHMENTNAME '.' IW52S-TYPE
     into wa_Files-FNAME.
    append wa_Files TO it_Files.

    wa_SOOD4-FOLTP   = lFolder-OBJTP.
    wa_SOOD4-FOLYR   = lFolder-OBJYR.
    wa_SOOD4-FOLNO   = lFolder-OBJNO.
    wa_SOOD4-OBJDES  = lFileDescription.
    wa_SOOD4-OBJNAM  = lFileDescription.
    w_h_data-objdes  = lFileDescription.
    CALL FUNCTION 'SO_DOCUMENT_REPOSITORY_MANAGER'
    EXPORTING
      METHOD               = 'IMPORTFROMPC'
      REF_DOCUMENT         = wa_SOOD4
    TABLES
      FILES                = it_Files
    CHANGING
       DOCUMENT            = wa_SOOD4
       HEADER_DATA         = w_h_data
       FOLMEM_DATA         = w_fol_data
       RECEIVE_DATA        = w_rec_data.

If the file was successfully read and stored in the abstracted folder location then the OKCODE in the wa_SOOD4 will either be 'CREA' or 'CHNG'. When successful, a few more variables are populated and the BINARY_RELATION_CREATE_COMMIT function is called with those variables to create the relationship between the document and the file of type ATTA.

The skip_transaction at the end is there because this example is using LSMW as a wrapper for a BAPI. This can be done just as easily as a report in SE38, but dealing with a BASIS team for transports to production when LSMW is available seems pointless.

    if wa_SOOD4-OKCODE = 'CREA' or wa_SOOD4-OKCODE = 'CHNG'.
      ws_BORIDENT-objkey      = wa_QMEL-QMNUM.  "Service Notification
      ws_BORIDENT-objtype     = 'BUS2080'.      "Business Class

            wa_Attachement-OBJTYPE = 'MESSAGE'.
            wa_Attachement-OBJKEY  = wa_SOOD4(34).
            CALL FUNCTION 'BINARY_RELATION_CREATE_COMMIT'
               EXPORTING
                  OBJ_ROLEA            = ws_BORIDENT
                  OBJ_ROLEB            = wa_Attachement
                  RELATIONTYPE         = 'ATTA'
               EXCEPTIONS
                  NO_MODEL             = 1
                  INTERNAL_ERROR       = 2
                  UNKNOWN              = 3
                  OTHERS               = 4.

      IF sy-subrc <> 0.
        if p_verbos eq 'X'.
          write: / IW52S-QMNUM, lFileDescription color col_negative,
           'Was not attached to the document, error #', sy-subrc.
        endif.
      ENDIF.
    else.
      if p_verbos eq 'X'.
        write: / IW52S-QMNUM, wa_Files-FNAME color col_negative,
         'File was not uploaded.'.
      endif.
    endif. "wa_SOOD4-okcode = 'CREA' or wa_SOOD4-okcode = 'CHNG'.
  endif. "IsLoaded ne 'X'.
endif.

skip_transaction.  "Just using LSMW as wrapper for BAPIs.

The end result

In the image below, the first file was attached on 06/05/2017 using the button while running the IW52 transaction in normal mode. The second document dated 06/06/2017 was loaded using the code above and this dummy data.

Attachment Row Id	Row Id - Primary Key	Activity Id - Foreign Key	SR #	Comments	Attachment Name	Size (In Bytes)	Type	Modified	Update File
1-106W6	1-106W3	1-106W3	1-99998TEST		1-99998TEST	37623	jpg	8/6/2001 2:37:10 PM	Y

Attach a file to a document using GOS.

Identifying the object type for other business objects

It would seem a safe bet that any business object that appears with the GOS button can have files attached using this code, but it may not be clear what OBJTYPE should be bundled with a document number (OBJKEY) to be passed in as the OBJ_ROLEA parameter of the BINARY_RELATION_CREATE_COMMIT function.

A quick search of the TOJTB table might be enough, but experimentation and some debugging might be required. The BDS_GOS_CONNECTIONS_GET function is a great place to start digging once a test document has been created with an attached file in the development sandbox . . .

CREDIT

This was a particularly difficult task as there is very little supportive documentation on the internet for it. This programmer owes a great deal of humility and respect to HariKrishna Malladi for providing an explanation of how to use the functions above in this great blog post.