Hi there,

A few weeks ago I created the following post: SAP Work Manager 6.2.0: How To Add Pictures to a Local Notification (using BDS)

In this blog, I will describe how to implement the closely related functionality that allows the user to have offline access to all the BDS notification pictures (or documents) after executing the transmit action.

Be careful with this because it can cause huge performance issues since we will retrieve the content of all the BDS documents linked to the user notifications. In my scenario, most of the documents, manuals and other work order related material are assigned via DMS and retrieved in a different way out of Agentry. Only the notification pictures are BDS attachments, therefore fetching the content won’t cause too much trouble.

The Problem:

Work Manager 6.2.1 introduces a new functionality to download attachments via synchronous call instead of the push request fetch that is causing a lot of problems to developers implementing the solution. If you are running Work Manager 6.2.0 there is a separate note to implement this: http://service.sap.com/sap/support/notes/2142473

But even with this enabled, the documents are only retrieved under user request, so if there is no connectivity it won’t be possible to fetch the attachment content. In my scenario, field technicians are working mostly offline and they need to have all the attachments available once the transmit finishes and they leave the facilities with internet access.

Development Overview:

  • The goal is to fetch the notification attachments during the transmit action. Since there is already a standard BAPI available to fetch the content of a document link, ABAP development won’t be necessary.
  • For a better performance and in order to use the Agentry Exchange Mechanism, the document content will be retrieved during the notification fetch process and stored in a session component manager for Document Links. Later on an Agentry fetch will get the documents from the component manager without calling the back end again.
  • Prerequisite. Only valid for Work Manager 6.2.1 or implementing note 2142473 for 6.2.0

Agentry UI changes:


The Agentry changes are minimum. Only a new fetch executed every transmit has been created with its corresponding steplet.


  • Created new Java steplet DocumentLinksFetchOnTransmitSteplet calling the adhoc created class com.syclo.sap.workmanager.customer.steplet.DocumentLinksFetchOnTransmitSteplet
  • Created new fetch ZDocumentLinksFetchOnTransmit
    • Run on trasmit
    • Add steplet DocumentLinksFetchOnTransmitSteplet as a Server Exchange Step




Java changes:

There are multiple Java changes divided in 2 separate concepts. The 1st one is to fetch and store the notification document links with the binary content into the DocumentLinks component manager during the notification fetch execution. The 2nd one is the transmit steplet executed during the newly created Agentry Fetch that will retrieve the previously stored Document Links to populate the Agentry collection.

1. Store Document Links with content to the component manager. Classes Notification and WorkorderNotification have to be extended

  • Created static class com.syclo.sap.workmanager.customer.DocumentLinkUtils in order to have a reusable method to fetch the document link content
    • Added constants to access a new configuration portal parameter that enables this functionality

public static final String ATTACHMENT_SYNCH_ENABLE_SECTION = "APPLICATION_CONFIG";
    public static final String ATTACHMENT_SYNCH_ENABLE = "ZAttachment.SynchOnTransmit";





    • Added method isFetchOnTransmitEnabled to check if the functionality is ON

public static boolean isFetchOnTransmitEnabled(User user) throws AgentryException{
        return user.getPropertyValue(ATTACHMENT_SYNCH_ENABLE_SECTION, ATTACHMENT_SYNCH_ENABLE).equalsIgnoreCase("Y");
    }





    • Added method getDocumentLinkContentToComponentManager(DocumentLink, User) to get the binary data of a given Document Link.

public static void getDocumentLinkContentToComponentManager(DocumentLink dl, User user){
 
        Logger log = new Logger(user, "getDocumentLinkContentToComponentManager");
 
        try{ 
            DocumentLinkComponentManager documentLinkCompManager = (DocumentLinkComponentManager)user.getComponentManager(DocumentLinkComponentManager.class.getCanonicalName());
     
            if(dl.isBDS() && dl.getFileMimeType().matches("image/(.*)")){             
         
                ArrayList<? extends SAPObject> recs = null;
                dl.setDocID(dl.getID());
                String agentryLastUpdate = user.eval("<<lastUpdate format=\"%m/%d/%Y %H:%M:%S\">>");
                GregorianCalendar lastUpdate = ConversionUtility.getTimeStampFromString(agentryLastUpdate, ConversionUtility.TIMESTAMPFORMAT);
         
                //Call the BAPI to read the binary data for the document
                BDSDocumentSynchFetchBAPI bapi = (BDSDocumentSynchFetchBAPI) BAPIFactory.create("BDSDocumentSynchFetchBAPI",
                        new Class<?>[] {User.class, GregorianCalendar.class, SAPObject.class},
                        new Object[] {user, lastUpdate, dl});                 
                bapi.run(dl);
                recs = bapi.processResults();
         
                if(recs.size() > 0){
                    //Store the retrieved document to the component manager,
                    //DocumentLinksFetchOnTransmitSteplet will access to the data afterwards
                    //documentLinkCompManager.addObject(recs.get(0));                     
                    Object val = recs.get(0);
                    if (val instanceof DocumentLink){
                        DocumentLink documentWithContent = (DocumentLink)val;
                        documentLinkCompManager.addObject(documentWithContent);
                        log.info("::Document ID = " + documentWithContent.getDocID());
                    } 
                }
            }
        }catch(Exception e){
            log = new Logger(
                    Server.getSAPServer(),
                    "com.syclo.sap.workmanager.customer.DocumentLinkUtils" +
                    "::getDocumentLinkContentToComponentManager(dl,user)::Thread " +
                    Thread.currentThread().getId() + "::");
            log.error(e.getMessage());
            e.printStackTrace();
        }
    }





  • Created class com.syclo.sap.workmanager.customer.object.Notification extending from the standard.
    • Override method addDocumentArrayList(ArrayList<DocumentLink> linkedDocuments) in order to retrieve each document link content and store it into the corresponding component manager to previously populate the objects collection in Agentry with an independent fetch.


@Override
    public void addDocumentArrayList(ArrayList<DocumentLink> linkedDocuments){
 
        Logger log = new Logger(_user, "addDocumentArrayList");
 
        try{
            if(linkedDocuments.size() > 0 && DocumentLinkUtils.isFetchOnTransmitEnabled(_user)){
                Iterator<DocumentLink> it = linkedDocuments.iterator();         
                while(it.hasNext()){
                    DocumentLink dl = it.next();
                    DocumentLinkUtils.getDocumentLinkContentToComponentManager(dl, _user);
                }
            }
        }catch(Exception e){
            log = new Logger(
                    Server.getSAPServer(),
                    "com.syclo.sap.workmanager.customer.object.Notification" +
                    "::addDocumentArrayList(linkedDocuments)::Thread " +
                    Thread.currentThread().getId() + "::");
            log.error(e.getMessage());
            e.printStackTrace();
        }
        super.addDocumentArrayList(linkedDocuments); 
    }





  • Created class com.syclo.sap.workmanager.customer.object.WorkorderNotification extending from the standard.
    • Override method addDocumentArrayList(ArrayList<DocumentLink> linkedDocuments) in order to retrieve each document link content and store it into the corresponding component manager to previously populate the objects collection in Agentry with an independent fetch. The code is exactly the same than the used for the Notification object.

  • Created class com.syclo.sap.workmanager.customer.bapi. BDSDocumentSynchFetchBAPI extending the standard
    • Skip reading the document link ID from the fetch properties if the value is not initial. This is necessary since we are not calling the BAPI directly from the Agentry fetch as the original assumes. Now it’s called from the Notification/WorkOrder fetch and the properties are already set in the BAPI class _documentLink attribute.


@Override
    public void init() throws Exception {
        //Only call super init() if _documentLink.ID is initial
        if(_documentLink.getDocID().equalsIgnoreCase("")){
            super.init(); //Gets the ID from the Agentry fetch
        }
     }


2. Steplet executed by the newly created Agentry fetch to populate document links into the Agentry collection.

  • Created class com.syclo.sap.workmanager.customer.steplet. ZDocumentLinksFetchOnTransmitSteplet
    • The steplet class will be called by the Agentry fetch ZDocumentLinksFetchOnTransmit  during transmit.
    • The steplet will call a new step handler class called ZDocumentLinksFetchOnTransmitStepHandler

@Override
    public boolean execute() throws AgentryException {
        try {
            ZDocumentLinksFetchOnTransmitStepHandler handler = (ZDocumentLinksFetchOnTransmitStepHandler) StepHandlerFactory.create(
                    StepHandlerFactory.getClassName("ZDocumentLinksFetchOnTransmitStepHandler"),
                    new Class[] { com.syclo.sap.User.class },
                    new Object[] { _user });
            ArrayList<SAPObject> recs = handler.run();
            int sz = recs.size();
            if (sz > 0) {
                _returnData = createSAPObjectArray(getSAPObject(), recs);
            }
            return true;
        }
        catch (Throwable e)
        {
            throwExceptionToClient(e);
            return false;
        }
    }






  • Created class com.syclo.sap.workmanager.customer.stephandler. ZDocumentLinksFetchOnTransmitStepHandler
    • This step handler class retrieves the document links from the corresponding component manager only if the parameter Attachment.Synchronous is enabled

public class ZDocumentLinksFetchOnTransmitStepHandler extends StepHandler {
    public static final String COMPONENT_MANAGER_CLASS_NAME = DocumentLinkComponentManager.class.getCanonicalName();
    protected DocumentLinkComponentManager _compManager;
    public ZDocumentLinksFetchOnTransmitStepHandler(User user) throws Exception {
        super(user);
        _compManager = (DocumentLinkComponentManager)_user.getComponentManager(COMPONENT_MANAGER_CLASS_NAME);
    }
    /**
     * Get the list of Document Links for the user by reading from the component manager
     * table associated with the user object. This method does not call any BAPI. This method
     * assumes that the user's hashtable has been populated previously from Notification and/or
     * Work Order fetch
     *
     * @return list of DocumentLink objects populated in fetch
     * @throws AgentryException
     */
    public ArrayList<SAPObject> run() throws AgentryException {
        String methodLabel = "run";
        Logger log = new Logger(_user, methodLabel);
        ArrayList<SAPObject> recs = new ArrayList<SAPObject>();
        if (DocumentLinkUtils.isFetchOnTransmitEnabled(_user)) {
            _user.inFetch(true);
            Hashtable<String, DocumentLink> headers = _compManager.getObjects();
            Enumeration<?> e = headers.elements();
            while (e.hasMoreElements()) {
                Object val = e.nextElement();
                if (val instanceof DocumentLink) {
                    DocumentLink dl = (DocumentLink)val;
                    recs.add(dl);
                    log.info("::Document ID = " + dl.getDocID());
                }
            }
        }
        log.info("::end");
        return recs;
    }
}






Configuration Portal Adjustments:

Finally, some adjustments have to be done in the configuration portal for the newly created classes and parameters

  • Created a client global to enable or disable the new functionality
    • Group: APPLICATION_CONFIG
    • Name: ZAttachment.SynchOnTransmit
    • Value: Y
  • Change the global parameter to assign the class for Notification object in order to use the new customer class
    • Group: SAPOBJECT
    • Name: Notification
    • Value: com.syclo.sap.workmanager.customer.object.Notification
  • Change the global parameter to assign the class for Work Order Notification object in order to use the new customer class
    • Group: SAPOBJECT
    • Name: Notification
    • Value: com.syclo.sap.workmanager.customer.object.WorkorderNotification
  • Created global parameter to assign the ZDocumentLinksFetchOnTransmitStepHandler step handler class dynamically.
    • Group: STEPHANDLER
    • Name: ZDocumentLinksFetchOnTransmitStepHandler
    • Value: com.syclo.sap.workmanager.customer.stephandler. ZDocumentLinksFetchOnTransmitStepHandler
  • Changed global parameter to assign the customer BDSDocumentSynchFetchBAPI class
    • Group: BAPI_CLASS
    • Name: BDSDocumentSynchFetchBAPI
    • Value: com.syclo.sap.workmanager.customer.bapi. BDSDocumentSynchFetchBAPI
  • Created global parameter to assign a BAPI wrapper to the new customer BDSDocumentSynchFetchBAPI class
    • Group: BAPI_WRAPPER
    • Name: com.syclo.sap.workmanager.customer.bapi. BDSDocumentSynchFetchBAPI
    • Value: /SMERP/CORE_DOBDSDOCUMENT_GET
To report this post you need to login first.

1 Comment

You must be Logged on to comment or reply to a post.

Leave a Reply