Programming Standard

Guideline for writing secure, optimized and scalable extensions.

Table of contents

  1. Extension Types
    1. Trigger Extensions
    2. Transaction Extensions
    3. Batch Extensions
    4. Utility Extensions
    5. Table Extensions
  2. Indentation and Formatting
  3. Naming
    1. Extensions
      1. Trigger Extensions
      2. Utility Extensions
      3. Transaction Extensions
      4. Table Extensions
      5. Methods
      6. Variables and Constants
  4. Extension Structure
    1. Utility Extensions
  5. Programming Practices
    1. Logging
      1. Log levels
    2. Database Access
    3. Loops
    4. Data Storage
      1. In Memory
      2. In Customer Extension Table
      3. In Dynamic Database
      4. In Text/XML/JSON File
    5. API Call
      1. ION API
      2. M3 API
    6. XML / JSON Parsing
    7. Date and Time
    8. Variable Types
  6. Testing
    1. Local
    2. Live
  7. Version Controlling
    1. Framework
    2. Structure
    3. Hosting
  8. Documentation

Extension Types

There are five types of extensions, below is a short description of each type.

Trigger Extensions

Trigger extensions are used to hook or inject our own code in a specific method in M3 BE via extension points.

Transaction Extensions

Transaction extensions are used to create custom M3 API transactions.

Batch Extensions

Batch extensions are used for long-running jobs and can be executed/scheduled via SHS010MI.

Utility Extensions

Utility extensions are used to create an extension program with collection of methods that can be called in other extensions. utility, String method, Object... arguments)

Table Extensions

Table extensions, or Dynamic Tables, are custom database tables.
It is possible to add columns, assign data types, unique keys, and indexes.

Indentation and Formatting

Tab size: 2 spaces Indent size: 2 spaces Continuation indent: 4 spaces Charset: utf-8

XtendM3 uses Groovy programming and ending code lines with semicolons are optional.



Extension names should be a valid Java class name. There are no restrictions on extension names, and they will run with no error when compiled.
However, when creating extensions the naming conventions below should be followed for readability and maintenance.


Program Type
IW Interactive
Fnc Fnc Batch
Batch Batch, Autostart Batch
MI MI Batch

Specification/Extension ID


  • Description should be in UpperCamelCase
  • Try to keep extension description names simple and descriptive
  • Avoid acronyms of abbreviations unless the word is a common jargon such as CO, DO, MO

Trigger Extensions

Format: <Module>_<Type>_<EXT ID>_<Description>


TypeProgramExtension name
Fnc BatchAPS450FncFIM_Fnc_1784_ValidateInvoice
MI BatchMMS100MISCE_MI_1784_ValidateDO

Utility Extensions

Format: <Description>Utils

Example: DateUtils

Transaction Extensions

  • EXT9XXMI is reserved for standard extensions and should not be used
  • When extending standard API, use same number suffix as standard program
    OIS100MI - EXT100MI

  • If there are same number suffix for standard API that needs to have extensions, use increment in number suffix
    MMS100MI - EXT101MI

Table Extensions

Format: EXT<XXX>

  • When extending standard table or creating custom table, include the following standard fields
Column NameDescriptionData TypeLength
RGDTEntry dateDecimal8
LMDTChange dateString8
CHIDChanged byString10
RGTMEntry timeDecimal6
CHNOChange numberDecimal3
  • For standard table extension, use EXT

  • For custom table extension, use EXT same suffix as extension API Table - EXT001 API - EXT001MI


  • Method name should follow Java naming convention for methods
  • Names should be in lowerCamelCase, using no underscores and starting with a letter
  • Only alphanumeric characters should be used. Alphanumeric characters include the letters a-z, either upper case or lower case, and the numerals 0-9
  • Should be simple and descriptive: validateDate, validateType

Variables and Constants

  • Variables and constants should follow Java naming convention
  • Variables should be in lowerCamelCase and constants in CAPITAL_CONSTANTS

Extension Structure

  • Global variables and main method are declared at the beginning of the program as compared to M3 Java where these are declared at the end of the program:
    public class ExampleTransaction extends ExtendM3Transaction {
      private final DatabaseAPI database;
      private final LoggerAPI logger;
      private Integer globalInteger;
      private String globalString;
      public ExampleExtension(DatabaseAPI database, LoggerAPI logger) {
        this.database = database;
        this.logger = logger;
      public void main() {
        globalInteger = 111;
        globalString = "example"

Utility Extensions

  • Utility extensions cannot contain global variables
  • It is not possible to declare and create instance of API in utility extensions, instead API should be passed as parameters in utility methods:
    public class ExampleUtility extends ExtendM3Utility {
      public String exampleGet(String miParamter, MICallerAPI miCaller) {
        String returnValue;
        Map<String, String> parameters = ["EXPA": miParameter];
        Closure<?> response = { Map<String, String> response ->
          returnValue = response.get("EXPA");
   "EXT000MI", "Get", parameters, response);

Programming Practices


  • LoggerAPI is used for logging messages
  • It is encouraged to add logs in extensions to help in debugging and troubleshooting issues particularly on non-development environments (TRN, PRD) where there is restriction on modifying extensions
  • Use proper level of logging – If extension is to be deployed in PRD, it should be limited to DEBUG level. Using INFO/WARNING/ERROR/TRACE on production environment can cause huge log size and impact MT Cloud environment
  • Avoid logging values of all returned fields in a database query
  • Logs can be accessed through Administration tool - Business Engine Jobs

Log levels

  • warning
  • error
  • info
  • trace
  • debug

Database Access

  • It is recommended to use standard APIs if transactions are available instead of direct database access on WRITE/UPDATE/DELETE where the program uses multiple tables to update. This could cause corrupt data if any on the logic, validation, or database update was missed or incorrectly updated
  • Make sure when using readLock that it is released at the end of the extension to avoid blocking another program access
  • When reading table with partial keys, verify that this will not return too many records. Include nrOfRecords parameter when performing readAll API calls
    • It is not allowed read more than 10,000 records per read. If there is a need to read more than 10,000 records, contact the review team with a good reasoning as to why it’s needed
  • When reading table, if possible, specify only the required column fields and avoid selectAllFields on querying tables particularly on tables which contains many fields


  • In M3 Java old programming, there are codes such as while(true) and performs multiple break inside the loop. When using while loop, avoid using condition=true and provide condition that will break the loop

Data Storage

In Memory

  • SessionAPI is used to store information in key-value mapping that can be accessed between multiple extensions in the same session
  • Make sure to use unique identifier as key names to avoid conflicts with other extensions when adding content to the cache
  • Reassess when there is too much information that needs to be stored in a session. It may be more practical to add the information using XtendM3 tables instead

In Customer Extension Table

  • Customer extension table refers to CUGEX1, CUGEX2 and CUGEX3 tables and can be accessed through standard API, CUSEXTMI
  • Customer extension table should not be used for high volume data and high number of reads, updates, and deletes instead replace with XtendM3 tables

In Dynamic Database

  • Dynamic Database or XtendM3 table is the equivalent of MAK custom table
  • Table name uses prefix EXT and can have specific columns and logical keys
    void read() {
      DBAction query = database.table("EXTMIT")
        .selection("EXITNO", "EXITDS")
      DBContainer container = query.createContainer();
      container.set("EXCONO", 999);
      container.set("EXDIVI", "GGG");
      container.set("EXSTAT", "30");
      container.set("EXRESP", "KFHAKANSSON");
      if( {
        String itemNumber = container.get("EXITNO");
        String itemDescription = container.get("EXITDS");
        mi.outData.put("RES1", itemNumber);
        mi.outData.put("RES2", itemDescription);

In Text/XML/JSON File

  • TextFilesAPI is the equivalent of MvxTextFile in M3 Java which can be used to read, write, or delete file
    public void readRecord(String folder, String record) {;, "UTF-8", readFile);
    Closure<?> readFile = { BufferedReader reader ->
      List<String> header = resolveFields(reader.readLine());
      while((line = reader.readLine()) != null) {
      readResult = header.toString();
      mi.outData.put("RESU", readResult);

API Call

  • M3 API can be run using IonAPI or MICallerAPI
  • It is preferred to use MICallerAPI which runs M3 API internally in the same layer as business engine instead of having another layer to execute M3 API


  • ION API provides secure access to M3 API and other registered application APIs through web services
  • When using IonAPI in extension, the response should be parsed XML/JSON Parsing
  • If standard M3 API is not available and an existing interactive program can be used to perform M3 update, ION API can be used to run IPS(Interactive Programming Services) which wraps the functionality of the program to perform M3 updates


  • MICallerAPI is the equivalent of utility program cMICaller in M3 Java

XML / JSON Parsing

Date and Time

  • Use classes LocalDate, LocalTime, LocalDateTime if required to retrieve current date or manipulate date or time
    int entryDate ="yyyyMMdd")).toInteger()
    int entryTime ="HHmmss")).toInteger()

Variable Types

  • It is not allowed to use def statements in XtendM3, the type should be known
    • If the type is unknown, use Object


Extension approval requires submission of approved Unit test and Functional test documents


  • Extensions can be locally tested using XtendM3 SDK which provides the interfaces for the internal APIs available to XtendM3 Extensions. This SDK can be used to build, test and debug Extensions locally without needing to have any M3 environment up and running
  • A sample repository ACME Corp. XtendM3 Extensions is provided to get you started on setting up project directories for the source codes, test scripts and XtendM3 SDK


  • If the program extension has been activated in the tenant, it automatically runs and the changes to the program are seen by the users. During unit testing, it is recommended that the extension should execute only for specific users until it is verified working to avoid interrupting other users when running the same program
  • Provided is an example of validating extension to run for specific user/s
    private boolean isEnabled() {
      if (program.getUser()  != "USERNAME") {
        return false
      return true

Version Controlling

More details are provided at Version Controlling page


Git version control system should be used


Refer to the example of an XtendM3 Extension Repository to version control extensions


Any git provider that the customer prefers GitHub, GitLab, Bitbucket and etc.


  • Extension JavaDoc on top of extension classes is required
    • Example:
     Extension Name: EXT000MI/transactionName
     Type: ExtendM3Transaction
     Script Author: 
     * Description of script functionality 
     Revision History:
     Name                    Date             Version          Description of Changes
     Revision Author         2022-01-01       1.0              Descriptive text here
     Revision Author         2022-02-02       1.1              Outlining what's been updated
     Revision Author         2022-03-03       1.2              In the current revision
  • Extension Methods JavaDoc on top of methods (except for main) is recommended