by | Jul 2, 2023

    Episode 1: Maximo Mobile Customisation

    Introduction

    Ready to get the most out of your Maximo Mobile experience? Join me as I delve into a treasure trove of development discoveries and practical tips to customise Maximo Mobile to your unique requirements. From datasource manipulation to adding new fields and relationships, this series of blogs will cover it all!

     

     

    Understanding Maximo-Datasources

    Let’s begin with Maximo-datasources, in this section you will learn how to add fresh fields and establish new relationships.

    Relationships

    While adding a custom field to a Maximo datasource is a breeze, the process of incorporating a field from a relationship can take on many forms.

    • Sub-attributes

    You can display a relationship field from the primary datasource by simply adding an attribute with the relationship name. This can then be expanded with sub-attributes from the corresponding table fields:

     

    <attribute name="serviceaddress" id="vqm4x">

    <attribute name="streetaddress" id="m_n7b"/>
    <attribute name="addressline2" id="xjpby"/>
    <attribute name="addressline3" id="wz84r"/>
    <attribute name="city" id="zp_ra"/>
    <attribute name="regiondistrict" id="nkgv2"/>
    <attribute name="stateprovince" id="y5vxe"/>
    <attribute name="postalcode" id="zp8w6"/>
    <attribute name="country" id="ve579"/>
    <attribute name="formattedaddress--formattedaddress" searchable="true" id="exe_8"/> <attribute name="latitudey" id="nzbrw"/> <attribute name="longitudex" id="ex9_v"/> <attribute name="description--svcaddressdesc" id="nbbxe"/>
    <attribute name="saddresscode--saddresscode" id="y6835"/>

    </attribute>

     

    Following the example below, the response JSON for this datasource will construct a new object filled with the attributes:

     

    {
       wonum: "12234",
       [...],
       serviceaddress: {  
       streetaddress: "ZZZZZ",  
       [...]
    }
    }

     

    Remember, this relationship needs to maintain a 1:1 relationship with the parent table.

     

    • Inline relationship

    Another method to bring in the fields from a relationship is through the inline mode:

     

    <attribute name="serviceaddress.formattedaddress--formattedaddress"
    searchable="true" id="g7dgk"/>
    <attribute name="serviceaddress.latitudey" id="g2xj2"/><attribute name="serviceaddress.longitudex" id="vjbxj"/>

     

    This method also requires a 1:1 relationship and will produce a response with an object containing the fields from the related table.

     

    • Utilising Field Aliases

    We can give a new [name] or alias to an attribute to dodge conflicts with other parent attributes. Here's an example of how to use this feature to avoid conflict with the parent description field:

     

    <attribute name="description" />
    <attribute name="serviceaddress" id="vqm4x">
           <attribute name="description--svcaddressdesc" id="nbbxe"/>
    </attribute>

     

    After using this feature, you'll notice that the JSON output will also change. The description attribute is no longer inside the serviceaddress object; it has been added as svcaddressdesc into the main JSON body:

     

    {
        wonum: "12234",
        svcaddressdesc: "Service address description", // field added because of the alias name
         [...],
         serviceaddress: [  
                {    
                    streetaddress: "ZZZZZ",
                     [...]  
                 }
          ]
    }

     

    • Tackling Multi line relationship

    For relationships that return more than one record, these will be added as an array in the JSON response. Here's an example of how this can be achieved:

     

    <attribute name="rel.failurelist{failurelist,failurecode.description,failurecode.failurecode}" id="yx4jk"/>
    <attribute name="rel.assetmeter{metername,active,rollover,lastreading,readingt
    ype,lastreadingdate,
    measureunitid,meter.measureunit.description--unitdescription,sequence, pointnum}" id="zp6y9"/>

     

     

    Saved Queries and Where Clauses

    Saved queries, which is created in the object structure used in a datasource, can be paired with a where clause to provide flexible and powerful data querying.

     

    <maximo-datasource-override id="todaywoassignedDS" saved-query="ASSIGNEDWOLIST" where="status!=&quot;DELAY&quot;" mobile-qbe-filter="" offline-immediate-download="true" default="true"/>

     

    However, note that the where clause is only applied on the Resource Application Roles and not on a device. To handle filters as objects on a device, you can use the mobile-qbe-filter parameter.

    To create an and clause with multiple conditions, we can create as many objects as we need with the first JSON field as an existing field from the datasource. If the field is not in the schema, we must add it:

     

    mobile-qbe-filter=""

     

     

    Synonym domains

    In order to keep the standard query from the database generation intact, we should avoid using a different saved query to create new data sources with the mxapisynonymdomain object structure. As an alternative, you can modify the MOBILEDOMAIN query, add your domain to the where clause, and override the datasource with a new where clause, a new saved query, or JavaScript.

    Here is an example overriding the existing datasource with maximo-datasource-override and using a different saved query and a JavaScript Controller class. We can also use the where and mobile-qbe-filter, but not with the Synonym Domain, as it can contain other synonyms used across the application.

     

    <maximo-datasource id="synonymdomainDS" lookup-data="true" object-structure="mxapisynonymdomain" offline-immediate-download="true" saved-query="MOBILEDOMAIN">  
            <schema id="bn686">    
                 <attribute id="xp2e9" name="value"/>    
                 <attribute id="g58pj" name="maxvalue" searchable="true"/>
                 <attribute id="wwwzq" name="description"/>    
                 <attribute id="m2ebr" name="domainid" searchable="true"/> 
                 <attribute id="aakrv" name="valueid" searchable="true" unique-id="true"/>    
                 <attribute id="r27qk" name="siteid" searchable="true"/>    
                 <attribute id="wz5gx" name="orgid" searchable="true"/>    
                 <attribute id="avz96" name="defaults" searchable="true"/> 
            </schema>  
            <maximo-datasource-overrid econtroller="ReasonCodeDataController"
    id="reasoncodeDS" offline-immediate-download="true" saved-query="QUERY" selection-mode="single"/>
         </maximo-datasource>

     

    And the Controller Class:

     

    class ReasonCodeDataController {
        onDatasourceInitialized(ds, owner, app) {  
              this.datasource = ds;  
              this.owner = owner;  
              this.app = app;
         } 
         async onAfterLoadData() {  
               this.datasource.setQBE('domainid', '=', 'RSNCODE');  
               await this.datasource.searchQBE();
          }
    }

    export default ReasonCodeDataController;

     

    For those who favour more fine-tuned control, AppCustomizations.js is a preferred tool for filtering data sources. Let's explore an illustrative example utilizing this utility.

     

    onBeforeLoadData(datasource, query) {
          if (datasource.name === 'dsFailureList') {  
             let qbe = datasource.parseQBE({ orgid:
    `=${this.page.state.workorder.orgid}` });  
             query.qbe = { ...query.qbe, ...qbe };
            }
           if (datasource.name === 'synonymdomainData' && query.qbe?.domainid[0]?.value === 'LOGTYPE') {  
              // set default query if not being filtered by another search  
              if (!query.qbe?.value) {    
                 let qbe = datasource.setQBE('value', 'in',
    ['CLIENTNOTE','UPDATE','WORK']);    
                 query.qbe = { ...query.qbe, value: qbe };  
             }
         }
    }

     

    AppCustomization.js incorporates the onBeforeLoadData method which is designed to intercept all data sources prior to their loading phase. Consequently, it provides the capability to modify the query that will be deployed to fetch the data.

    In the first instance, when datasource.name equates to 'dsFailureList', we encounter a unique data source. This allows us the opportunity to intercept and subsequently amend the query with a function pre-existing in the data source class.

     

    let qbe = datasource.parseQBE({ orgid: `=${this.page.state.workorder.orgid}` });
    query.qbe = { ...query.qbe, ...qbe };

     

    The parseQBE function will accept a JSON object.

     

    INPUT: {siteid: "BEDFORD", assettype:"=PUMP", assethealth: ">20"}

     

    For the second scenario, we're examining a condition where datasource.name matches 'synonymdomainData' and query.qbe?.domainid[0]?.value corresponds to 'LOGTYPE'. In this instance, we're specifically intercepting the 'LOGTYPE' domain. This is key to remember as the synonym domain data source can encompass several domains.

    The utilization of qbe?.domainid[0]? ensures that any data source devoid of a qbe query or a qbe query lacking a domainid value won't disrupt the execution. Consequently, it's feasible to adjust the data source and introduce a new condition to the original query.

     

    // set default query if not being filtered by another search
    if (!query.qbe?.value) {
        let qbe = datasource.setQBE('value', 'in', ['CLIENTNOTE','UPDATE','WORK']);
        query.qbe = { ...query.qbe, value: qbe };
    }

     

    Specifically, in this case, to avoid constant overriding of the 'LOGTYPE' domain, we ensure a filter is in place in the value field.

    Unlocking the potential of Maximo Mobile with customisation can significantly enhance your productivity and the overall user experience. Whether it's adding new fields or relationships, using saved queries, or employing JavaScript for data source manipulation, each technique adds a layer of flexibility and power to your Maximo Mobile application.

     

    JavaScript datasource manipulation

    We can harness the power of JavaScript to manipulate the data source data. This can be done either using the method onBeforeLoadData or by creating a custom Controller. Moreover, data can also be manipulated after it's loaded with the method onAfterLoadData. Here's an example of using it with custom data sources:

     

    <maximo-datasource id="myalndomainsds" offline-immediate-download="true"
    lookup-data="true" object-structure="mxapialndomain" page-size="100" selection-mode="single">
          <schema id="p3apw">  
               <attribute name="value" searchable="true" id="ggzba"/>  
               <attribute name="valueid" unique-id="true" id="mnxqw"/>  
               <attribute name="description" id="jqr6a"/>  
               <attribute name="domainid" searchable="true" id="jg4rn"/>  
               <attribute name="siteid" id="zbyr3"/>  
               <attribute name="orgid" id="ryqp4"/>
          </schema>
          <maximo-datasource-override id="resolutioncategoryds" offline-immediate-download="true" where="domainid=&quot;RESOLUTIOCAT&quot;" selection-mode="single"/>
           <maximo-datasource-override id="restorecodeds" offline-immediate-download="true" where="domainid=&quot;RESTORECODE&quot;" selection-mode="single"/>
           <maximo-datasource-override id="restorecauseds" offline-immediate-download="true" where="domainid=&quot;RESTORECAUSE&quot;" selection-mode="single"/>
    </maximo-datasource>

     

    JavaScript:

     

    async onAfterLoadData(datasource, item, query) {
         if (datasource.name === 'resolutioncategoryds') {  
           datasource.setQBE('domainid', '=', 'RESOLUTIOCAT');  
           await datasource.searchQBE();
        } else if (datasource.name === 'restorecodeds') {  
           datasource.setQBE('domainid', '=', 'RESTORECODE');  
           await datasource.searchQBE();
        } else if (datasource.name === 'restorecauseds') {  
           datasource.setQBE('domainid', '=', 'RESTORECAUSE');  
           await datasource.searchQBE();
        }
    }

     

    Bear in mind that even with a where clause in the data source, the data won't be filtered on the mobile device. Therefore, we have the option to either use the mobile-qbe-filter or JavaScript.

    This blog is part of a series aimed at enabling you to customise your Maximo Mobile application to your specific needs. If you're interested in delving deeper into this topic, don't hesitate to subscribe to our newsletter. We're here to help you on your journey towards mastering Maximo Mobile customization!

     

    For more articles on Maximo Mobile: 

    Digitising Inspection Forms with Mobile Devices

     

     

    Sign up to our free newsletter to explore emerging technologies, industry events and Maximo best practice.