Maximo Open Forum

 View Only
  • 1.  questions on using Email Listener

    Posted 11-30-2021 09:30
    I'm in the midst of creating training documents & videos for my company, and I've come to Email Listener -- which we've never used, and based on this article from IBM it seems like getting it to work would at least be challenging for us since we use MS 365.  Just a couple of simple questions for anyone out there who has successfully set this up, because between the System Help files and IBM Training Academy videos, I seem to be getting conflicting information:

    1) On an incoming unformatted email, for Maximo to properly identify it as an update to an existing ticket (SR) instead of creating a new ticket, does the Object Key Identifier have to surround the key value, or just be in front of it?  System Help says surround (example given is ##1009##), IBM Academy training video says just in front (example given is ##:ticketid).
    2) On that same email, does the Object Key Identifier and key​ value HAVE TO be in the Subject line, or can it be in the body?​
    #Administration

    ------------------------------
    Travis Herron
    Pensacola Christian College
    ------------------------------


  • 2.  RE: questions on using Email Listener

    Posted 12-01-2021 08:44
    2) On that same email, does the Object Key Identifier and key​ value HAVE TO be in the Subject line, or can it be in the body?​

    It should be in the subject in order to check and take decision via IBEP workflow to create new ticket or add the email as log.

    ------------------------------
    Fouad Jaara
    BTAM
    ------------------------------



  • 3.  RE: questions on using Email Listener

    Posted 12-01-2021 09:11
    Edited by Jade Warren 12-01-2021 09:16
    The object key delimiters must SURROUND the primary key, thus ##keyvaluehere## ... out-of-the-box, the key value must exist in the SUBJECT of the mail item.

    We have found ourselves significantly disappointed with the limitations of the listener; it is VERY picky about what it will accept and process, and we have identified at least three business cases that the listener cannot address:
    1) Adding communications to closed tickets--one would think that this would be acceptable and useful, but if a ticket is closed, the listener rejects it
    2) Processing e-mails from "external parties"--we are a cooperative and so our service desk occasionally handles requests from staff at our 28 member owners...we simply CANNOT be the HR department for 28 independent companies, but unless EVERY incoming e-mail has a corresponding person record in Maximo, the listener will reject
    3) Adding communications to WORK ORDERS ... I cannot fathom why IBM doesn't enable this, but the out-of-the-box functionality is such that either the object key matches to a ticket (happy path) or it doesn't and the mail item is rejected (sad path)

    Because of this, we ​have supplemented the listener with several automation scripts to address the business cases above.  Obviously these scripts encapsulate our specific requirements, but the overall idea may be useful.

    INBOUNDCOMM.SAVE

    from psdi.server import MXServer;
    from psdi.mbo import MboConstants;
    from java.util import HashMap;

    ## get context
    mailid = mbo.getString("MAILID");
    obj_key = mbo.getString("OBJECTKEY");
    object_name = mbo.getString("OBJECTNAME");
    status = mbo.getString("STATUS");
    subject = mbo.getString("SUBJECT");
    message = mbo.getString("MSGBODY");
    sender = mbo.getString("CHANGEBY");
    vError = mbo.getString("ERROR");

    ## flags
    found = False;
    record_exists = False;
    record_closed = False;
    create_comm = False;
    create_record = False;
    proceed = True if mailid == "" else False;

    ## correct situations where the OBJECTKEY wasn't populated due to poor coding
    if (obj_key == ""):
        s = subject.find("##");
        e = subject.find("##", s + 2);
    if (s != -1 and e != -1): ## double delimiters exist...
        obj_key = subject[s+2:e];
        mbo.setValue("OBJECTKEY", obj_key, 11L);

    if (not mbo.isNull("OBJECTKEY")):
        tkt_set = mbo.getMboSet("$TKT", "TICKET", "ticketid = '" + obj_key + "'");
        tkt = tkt_set.moveFirst();
        wo_set = mbo.getMboSet("$WO", "WORKORDER", "wonum = '" + obj_key + "'");
        wo = wo_set.moveFirst();

    if (tkt != None): ## Check ticket table
        object_name = tkt.getString("CLASS");
        mbo.setValue("OBJECTNAME", object_name);
        record_exists = True;
        record_closed = True if tkt.getBoolean("HISTORYFLAG") else False;
        UID = tkt.getLong("TICKETUID");
        varRecord = tkt;
    elif (wo != None):
        object_name = wo.getString("WOCLASS");
        mbo.setValue("OBJECTNAME", object_name);
        record_exists = True;
        record_closed = True if wo.getBoolean("HISTORYFLAG") else False;
        UID = wo.getLong("WORKORDERID");
        varRecord = wo;
    else:
        record_exists = False;

    ## start doing record clean-up
    if (subject == ""):
        subject = "<empty subject>";
        mbo.setValue("SUBJECT", subject, 11L);

    if (sender == "" or sender == "MAXADMIN" or vError.find("Person does not exist") != -1):
        sender == "EXTERNAL";
        mbo.setValue("CHANGEBY", "EXTERNAL", 11L);
        mbo.setValue("PERSONID", "EXTERNAL", 11L);

    if (mailid == "" and status == "NEW" and subject[:13] == "Undeliverable"):
        mbo.setValue("STATUS", "COMP_ERR", 11L);
        mbo.setValue("ERROR", "Processed by GRE script", 11L);
        mbo.setValue("MAILID", mailid + "undeliverable_", 11L);

    ## Truncate SEND TO field
    s = mbo.getString("SENDTO");
    if (len(s) > 255):
        mbo.setValue("SENDTO", s[:250] + "...", 11L);

    ## start taking action against the record
    if (mailid != "" and status == "WORKFLOW" and vError != ""):
        mbo.setValue("STATUS", "COMP_CL", 11L);
        mbo.setValue("ERROR", "Processed by GRE script", 11L);
        proceed = False;
        mbo.setValue("MAILID", mailid + "workflow_", 11L);
       
    if (not record_exists and obj_key != "" and mailid == ""): ## in this case, a bogus object key was fed to the listener
        mbo.setValue("STATUS", "ERROR_GRE", 11L);
        mbo.setValue("ERROR", "Object key " + obj_key + " does not exist in either the TICKET or WORKORDER table. No action.");
        proceed = False;
        mbo.setValue("MAILID", mailid + "bad_key_", 11L);

    if (proceed and record_exists) and (
    (status == "INVALID") or
    (status in ("ERROR", "NEW") and mbo.getString("PERSONID") == "EXTERNAL") or
    (record_closed) or
    (object_name not in ("SR", "PROBLEM"))
    ):
    ## in this case, the record exists in Maximo and we DO want a COMM LOG entry
        create_comm = True;

    if (proceed and not record_exists and ((status == "INVALID") or (status in ("ERROR", "NEW") and mbo.getString("PERSONID") == "EXTERNAL")) and mailid == ""):
        create_record = True;
        create_comm = True;

    #### ACTIONS - CREATE TICKET ####

    if (create_record): ## Create ticket ##
        ctx = HashMap();
        ctx.put("mbo", mbo);
        service.invokeScript("LSTNCRSR", ctx);

    obj_key = mbo.getString("OBJECTKEY");
    varRecord = mbo.getMboSet("GRE_SR").moveFirst();

    #### ACTIONS - COMM LOG ####
    if (create_comm): ## Create communication log entry ##
        if (not record_closed):
            c = varRecord.getMboSet("COMMLOG").add(11L);
        else:
            c = mbo.getMboSet("OPEN_TICKETS").moveFirst().getMboSet("COMMLOG").add(11L);

        src_fields = ["CC", "BCC", "SUBJECT", "CHANGEBY", "CREATEDATE", "SENDFROM", "SENDTO", "REPLYTO", "MSGBODY"];
        dst_fields = ["CC", "BCC", "SUBJECT", "CREATEBY", "CREATEDATE", "SENDFROM", "SENDTO", "REPLYTO", "MESSAGE"];

        c.copyValue(mbo, src_fields, dst_fields, 11L);
        c.setValue("INBOUND", 1, 11L);

        ## Add attachments to newly created comm log entry
        aset = mbo.getMboSet("DOCLINKS");
        xset = c.getMboSet("DOCLINKS");

        a = aset.moveFirst();
        while (a != None):
            x = xset.add();
            fields = ["DOCUMENT", "DOCTYPE", "DOCINFOID"];
            x.copyValue(a, fields, fields, 11L);
            a = aset.moveNext();
        if (vError == ""):
            vError = "COMM LOG updated.";
        else:
            vError = vError + " " + "COMM LOG updated.";

        if (record_closed):
            c.setValue("OWNERID", UID, 11L);
            c.setValue("OWNERTABLE", object_name, 11L);

    #### FINAL CLEAN-UP ####

    if (create_record == 1 or create_comm == 1):
        mbo.setValue("MAILID", mailid + "processed_", 11L);
        mbo.setValue("ERROR", vError, 11L);
        if (create_record == 1):
            mbo.setValue("STATUS", "COMP_NEW", 11L);
        else:
            mbo.setValue("STATUS", "COMP_CL", 11L);

    LSTNCRSR

    from psdi.server import MXServer;
    from psdi.mbo import MboConstants;
    from psdi.mbo import Mbo;
    from psdi.util import HTML
    from java.util import HashMap;

    try:
        set = MXServer.getMXServer().getMboSet("SR", mbo.getUserInfo());
        SR = set.add();

        ## Ticket basic identifiers
        SR.setValue("SITEID", "TRANS", 11L);
        SR.setValue("EXTERNALSYSTEM", "EMAIL", 11L);
        SR.setValue("SOURCE", "EMAIL", 11L);

        ## E-mail fields
        SR.setValue("DESCRIPTION", mbo.getString("SUBJECT"));

        ## Process e-mail body to remove excess line breaks
        TEXT = mbo.getString("MSGBODY");
        SR.setValue("DESCRIPTION_LONGDESCRIPTION", TEXT);

        ## Process e-mail body to remove excess line breaks
        ctx_sr = HashMap();
        ctx_sr.put("mbo", SR);
        service.invokeScript("CLEANTXT", ctx_sr);

        ## Update other e-mail sourced fields and ticket fields
        SR.setValue("REPORTDATE", mbo.getDate("RECEIVEDATE"));
        SR.setValue("REPORTEDBY", mbo.getString("PERSONID"));
        SR.setValue("DEPT_GRE", "IT");
        SR.setValue("COMMSEND_GRE", True);
        SR.setValueNull("OWNER");

        priority = 2 if mbo.getString("PRIORITY")[:1] == 1 else 4;
        SR.setValue("URGENCY", priority);

        ## Flag the inbound comm row
        mbo.setValue("OBJECTKEY", SR.getString("TICKETID"), 11L);
        mbo.setValue("OBJECTNAME", "SR", 11L);
        mbo.setValue("ACTION", "CREATE", 11L);
        mbo.setValue("STATUS", "COMPLETE", 11L);

        ## Determine what person group should be assigned ##
        vEmail=mbo.getString("emailaddress");
        if (vEmail in ("max2252", "qmaxit")):
            SR.setValue("PERSONGROUP_GRE","2252");
        if (vEmail=="max1179"):
            SR.setValue("PERSONGROUP_GRE","1179");
            SR.setValue("URGENCY", 4, 11L);
            SR.setValue("COMMODITYGROUP", "GRE", 7L);
            SR.setValue("ASSETNUM", "THREAT", 7L);
            SR.setValue("TKTTYPE_GRE", "INC", 11L);
        if (vEmail=="maxnoc"):
            SR.setValue("PERSONGROUP_GRE","5662");

        ## Process self-assignments #ME tag ##
        vD = mbo.getString("SUBJECT");
        vDu = vD.upper();
        l = len(vD);
        x = vDu.find("#ME");
        if (x != -1):
            ## Clear the PERSONGROUP_GRE field
            SR.setValue("PERSONGROUP_GRE","");
            ## Determine whether the REPORTEDBY individual belongs to a QUEUE group
            try:
                clause="persongroup in (select persongroup from persongroupteam where '" + mbo.getString("PERSONID") + "' = respparty) and type='QUEUE' "
                persongset = MXServer.getMXServer().getMboSet("PERSONGROUP", mbo.getUserInfo());
                persongset.setWhere(clause);
                g = persongset.moveFirst()
                if (g != None): ## As long as the person belongs to AT LEAST 1 QUEUE group, we can proceed with ownership assignment
                    SR.setValue("OWNER", mbo.getString("PERSONID"));
                if (persongset.count()==1): ## If the person belongs to EXACTLY 1 QUEUE group, we can also assign their group for them
                    SR.setValue("PERSONGROUP_GRE", g.getString("PERSONGROUP"));
                if (g == None): ## If the person belongs to NO QUEUE groups (probably due to drag-and-drop), assign to '2252'
                    SR.setValue("PERSONGROUP_GRE", "2252");
            finally:
                persongset.close();

            ## Remove tag from the DESCRIPTION field
            SR.setValue("DESCRIPTION", SR.getString("DESCRIPTION").replace("#ME", ""));
            set.save();
    finally:
        set.cleanup();
        set.close();

    We have actually done additional things like processing approvals via the inbound communication process--overall, we have been happy with the results of these scripts.

    Jade

    ------------------------------
    Jade Warren
    Great River Energy
    ------------------------------



  • 4.  RE: questions on using Email Listener

    Posted 12-01-2021 10:49
    That is fantastic-looking stuff there!

    Re: adding comments to Closed tickets -- that's pretty standard Maximo, can't edit something that's closed; I'm not surprised that that's not allowed.  One specific question for you Jade:  In this portion of the Script:
    #### ACTIONS - COMM LOG ####
    if (create_comm): ## Create communication log entry ##
        if (not record_closed):
            c = varRecord.getMboSet("COMMLOG").add(11L);
        else:
            c = mbo.getMboSet("OPEN_TICKETS").moveFirst().getMboSet("COMMLOG").add(11L);​

    Am I reading this right, that if the record is in Closed status, it's going to add the CommLog entry to the first open Ticket it can find?



    Re: email from external parties -- at least it's documented!  For my situation, I've currently got thousands of college students, more may come or go at any semester change, and I don't have Maximo integrated to any directory system that'll keep this updated for me.  Not sure that I'd want to take on managing it manually or semi-manually (e.g. data dump 2 or 3 times per year).  But it looks like you've got a way around that in your script. . .interesting!

    Re: can't add Communications to Work Orders -- now that's an interesting issue.  I've got to assume it's mostly about licensing.  So help me play this out (everyone):  If I had a technician in the field with a Work Order, and he needs to communicate/collaborate with the "customer" and I provide a Comm Template for this.  That Comm Template is going to need the Object Key Identifier and TicketID, so when the customer responds, the Ticket (SR) would be updated with their reply.  I assume getting the TicketID in the Comm Template can be done via relationship dotted notation, and that the correspondence from both technician and customer would be visible on both the Work Order and SR.  However, what if there are many SR's mapped (related) to one Work Order. . .like two roommates turn in a request for the same issue in their room, or two teachers turn in the same request for a classroom?  Which "customer" is going to be collaborated with?  It seems to me that at that point, the communications need to happen on the Work Order -- but the customer likely isn't authorized/licensed to make updates to Work Orders.  How does this work in real life?

    ------------------------------
    Travis Herron
    Pensacola Christian College
    ------------------------------



  • 5.  RE: questions on using Email Listener

    Posted 12-01-2021 10:55

    So that's a bit of sleight of hand-Maximo business logic will NOT allow a comm log entry to be added to a closed ticket, AND will not allow a comm log entry to be added w/o an "owner" record...

     

    So, I just pull any random open ticket, create my comm log entry, and then "fix" the data at the end using the lines:

    if (record_closed):
            c.setValue("OWNERID", UID, 11L);
            c.setValue("OWNERTABLE", object_name, 11L);

     

    ��

     

    Jade

    NOTICE TO RECIPIENT: The information contained in this message from Great River Energy and any attachments are confidential and intended only for the named recipient(s). If you have received this message in error, you are prohibited from copying, distributing or using the information. Please contact the sender immediately by return email and delete the original message.





  • 6.  RE: questions on using Email Listener

    Posted 12-01-2021 13:47
    I just found this IBM article that shows the mxe.lsnr.validateperson System Property can be set to 0 so that a Person record isn't required.

    ------------------------------
    Travis Herron
    Pensacola Christian College
    ------------------------------



  • 7.  RE: questions on using Email Listener

    Posted 12-02-2021 09:56
    Correct, that should be set to 0 and any email address (including ones you might not want such as spam) can send to it. One of the enhancements I've added to email listener was to manage a block list of email addresses & images (think twitter images for example in email signatures). This is also helpful if someone sends something to another automated system accidentally as you get a never ending loop between the two systems :)

    Regarding using it for Office 365, 7.6.1.2 added support for OAuth for email listener which is critical as Office 365 is removing the support for utilizing basic authentication for email inboxes. It's a bit painful the first time to setup OAuth in Office 365 if you haven't done it before but hopefully your IT team has someone who has done it already because once you've done it, it's not too bad. Just learning how to define the applications and such in Azure AD and which permissions you need takes a bit to learn. I would also suggest trying to be on a recent IFIX as there were a couple of snags in the initial implementation of OAuth with Office 365 I reported.

    ------------------------------
    Steven Shull
    IBM
    ------------------------------



  • 8.  RE: questions on using Email Listener

    Posted 12-06-2021 13:38
    Somewhat related. . .I'm trying to figure out Email Interactions now, and I don't quite understand how the @@ variables work.  For example, one of the OOTB Comm Templates has a variable @@GUIDANCE@@.  The System Help tells me what it does -- basically, add comments to help the recipient know what to do -- but where do I get to modify the text of what is substituted in for the variable?  I don't see it in System Properties, nor a quick glance of the MAXVARS table.

    (The same question could be asked of the @@TASKLIST@@ and the @@OBJID@@ variables.)

    Alternatively, if I wanted to write my own email body (assuming I have no way to manipulate these IBM-supplied variables), I'm not sure how to do it properly.  Just start with mxe.mfmail.ValueListBegin, write "operation assitance" comments (yeah, razzing on IBM's typo) by starting a line with the mxe.mfmail.AssistMarker value, and when done end with mxe.mfmail.ValueListEnd?  It seems like you'd have to use the @@SELECTIONLIST@@ and @@MAILKEY@@ as-is.

    Finally. . .is anybody even using this?  It feels "legacy."
    ​​​​​​

    ------------------------------
    Travis Herron
    Pensacola Christian College
    ------------------------------



  • 9.  RE: questions on using Email Listener

    Posted 12-06-2021 13:57
    The short story is that I've used scripting to augment what I see as significant deficiencies in communication templates.  My approach goes something like this:
    1) Think of a random "tag" to use inside my e-mail body (I personally chose the convention #TAG_HERE#
    2) Create a script that fires on ​COMMLOG.MESSAGE -- this script looks for each "tag" that I've created and processes it based upon script logic (note that one COULD theoretically create a separate Maximo table to store tags + "what should be done", but I haven't gone that far down the rabbit hole)
    3) Enjoy the results :-)

    The first part of my script defines some methods to take inputs and create HTML tables, HTML unordered lists, or CSV strings.
    Then, I instantiate variables.
    Then, based upon where the comm log is coming from, I assign variables.
    Then, build content.
    Then, substitute in.

    I currently have about 40 custom tags that I'm processing.
    The nice thing is that because of the way I have the script structured, each new tag only requires about 6 lines of code to invoke.

    I attached the script as a TXT file to my reply.  I also attached a PDF of an example e-mail that shows how the tables end up inserted into the communication.

    ------------------------------
    Jade Warren
    Great River Energy
    ------------------------------

    Attachment(s)

    pdf
    SampleEmail.pdf   706 KB 1 version
    txt
    COMMLOG_MESSAGE.txt   38 KB 1 version