Odin is a framework for building web applications for M3, that consists of three parts:
Odin is not a framework of UI components nor does it require the application developer to use a specific UI framework. There is however optional support for the Infor IDS controls (previously know as SoHo), using jQuery or Angular.
The M3 Odin SDK consists of three different NPM packages. The packages are published to the NPM registry and can be installed through npm. See more details about package installation in the following sections.
These are the names of the packages and their npm links:
The M3 Odin CLI is a command line interface for creating and working with projects. The CLI needs to be installed before it can be used.
Run the following command to install the CLI globally on your local computer.
npm install -g @infor-up/m3-odin-cli
Alternatively, to get the most up-to-date (but potentially unstable) version, add the @next
tag:
npm install -g @infor-up/m3-odin-cli@next
Run the following command to verify that the CLI was installed successfully. The command should output the help for the CLI.
odin -h
The M3 Odin CLI can be used to create different types of projects. The quickstart shows how to create an Angular application project using the Infor Design System (IDS) components.
odin new
npm install
odin serve
The M3 Odin CLI can be used to create different types of projects that includes different frameworks and functionality.
Note that the project names must adhere to the rules for the name property of a package.json file. A summary of the rules are that the name must start with a character, only contain lower case characters that are URL safe. We would recommend sticking to characters and dashes. More details about names can be found here https://docs.npmjs.com/files/package.json#name
Navigate to the folder that should contain the project and open a command interface. Run the following command to start the task wizard that can be used for the following;
Create a new project, where the project name will be the context root for the application if deployed in H5. Follow the wizard to create a new project.
odin new
The following information will be requested:
Create a non Angular, plain vanilla web project.
odin new my-project-name
When an application is served locally using odin serve
, it cannot make requests directly to the backend service (M3 or ION API). Cross-origin requests are usually blocked, and the sign-in flow requires redirects that are not needed when the application is deployed in H5. To bypass this, all requests to /m3api-rest
, /mne
etc. are sent through a proxy server started with odin serve
. The target of the proxied requests are configured in the odin.json file, using the odin set
command.
Depending on the environment being used (see flow chart below for specifics), authentication needs to be set up either through basic authentication or the odin login
command.
When targeting an on-premise M3 environment, basic authentication is usually enabled, which means that the client will send credentials directly to the server. There is no need for any further configuration or odin login
in this case.
Authentication for a cloud environment is done through OAuth2 using the odin login
command. This command does the following:
--m3
is provided).--update-config
is provided).Now, when running odin serve --multi-tenant
, the proxy will modify the requests and add the ION API bearer token or M3 session cookie to the request headers.
The generated API documentation can be viewed here:
https://infor-cloud.github.io/m3-h5-sdk/m3-odin/docs/
We recommend Visual Studio Code (https://code.visualstudio.com/) but any editor can be used.
The samples are packaged as one Angular application and available as m3-odin-sample-soho-2.0.0.zip. The sample zip files can be downloaded from this directory: https://github.com/infor-cloud/m3-h5-sdk/tree/master/m3-odin/samples
The sample are packaged as an application. To run it unzip the sample zip and run it as an application.
Unzip the sample
In the project folder run the following command
npm install
Update the proxy configuration
odin set ion-proxy https://ionserver/TENANT
odin set m3-proxy https://m3server
For the ION API example to work the source has to be manually edited to provide the ION Token
Run the server
odin serve
Browse to http://localhost:8080
Run the following command to build the project for production. A zip will be created in the dist folder. Note that the name of the zip as the name will be used as the application name when uploaded in H5. You can change the name of the zip file if necessary.
odin build
This section describes how to install the application in the H5 client. It is required to be an M3UI-Administrator to have access to the H5 Administration. Take the zip file in the dist folder created by the build command and upload it using the H5 Administration tool. The name of the zip file will be used as the path to the application.
To open an installed application within the H5 tab system press Crtl+R and paste in the URL to the application.
/mne/apps/my-project-name
Antoher option is to go to Start to access the Start pages and add a page with a Custom menu widget in which it is possible to add a New link with the link set to /mne/apps/my-project-name.
The user context contains the information for the user in MNS150. The MIService will use and pass the current company as matrix parameters to the M3 MI programs unless specific CONO and DIVI are passed in the MIRequest. Keep the reference to the UserContext object and it will be automatically updated if company and division are changed during the session.
It is recommended to load the UserContext as part of the application initialization.
import { Component } from "@angular/core";
import { ArrayUtil, CoreBase, IUserContext } from "@infor-up/m3-odin";
import { UserService } from "@infor-up/m3-odin-angular";
@Component({
templateUrl: "./user-context.component.html",
})
export class UserContextSampleComponent extends CoreBase {
userContext = {} as IUserContext;
constructor(private userService: UserService) {
super("UserSampleComponent");
this.userService.getUserContext().subscribe(
(userContext: IUserContext) => {
this.userContext = userContext;
const lang = userContext.currentLanguage;
const divi = userContext.currentDivision;
const cono = userContext.currentCompany;
const usid = userContext.USID;
this.logInfo(
"User context: " +
usid +
", cono: " +
cono +
", divi: " +
divi +
", lang: " +
lang
);
},
(errorContext: IUserContext) => {
// Handle error
this.logError(errorContext.errorMessage);
}
);
}
}
Call M3 MI transactions to list, change and get M3 data. By default the framework will pass matrix parameters (visible as part of the URL path) for the current company and division if the user context has been retrieved. The example below assumes that the user context has already been retreived in another component, thus it will be automatically available within the MIService without having to explicity call it.
To override those default values pass in CONO and DIVI in the MIRecord that is set as input on the MIRequest.
When creating the input (MIRecord) for the transaction there are a number of set methods that can be used to set string, date and numbers. The format of input data is import for date fields and numeric fields that may contain decimals. The date format for date fields must be "yyyyMMdd" and the decimal separator for decimal fields must be ".". There are several set functions on the MIRecord class that can be used to set input data on the correct format.
let inputRecord = new MIRecord();
inputRecord.setString("CUNO", "ACME");
inputRecord.setDate("TEEC", new Date());
inputRecord.setNumberString("AGBG", 123123.123);
The MI transactions may return a lot of data. Always specify the output fields to increase performance and reduce bandwidth. In the example below customer details are retreived for a customer but only part of the available output fields will be used.
import { Component, OnInit, ViewChild } from "@angular/core";
import { CoreBase, IMIRequest, IMIResponse, IUserContext, MIRecord } from "@infor-up/m3-odin";
import { MIService } from "@infor-up/m3-odin-angular";
@Component({
templateUrl: "./customer.component.html"
})
export class CustomerSampleComponent extends CoreBase implements OnInit {
constructor(private miService: MIService) {
super("CustomerComponent");
}
ngOnInit() {
this.getDetails();
}
private getDetails() {
const inputRecord = new MIRecord();
const customer = "ACME";
inputRecord.setString("CUNO", customer);
const request: IMIRequest = {
program: "CRS610MI",
transaction: "GetBasicData",
record: inputRecord,
outputFields: ["CUNM", "CUNO", "CUA1", "CUA2", "CUA3", "CUA4", "YREF", "CSCD"]
};
this.setBusy(true);
this.miService.execute(request).subscribe((response: IMIResponse) => {
this.setBusy(false);
if (!response.hasError()) {
this.logInfo("Customer Basic data for " + customer);
const record: MIRecord = response.item as MIRecord;
const address1 = record["CUA1"];
const address2 = record["CUA2"];
this.logInfo("Address 1 " + address1);
this.logInfo("Address 2 " + address2);
} else {
this.handleError(response, customer);
}
// Handle error
}, (response) => {
this.setBusy(false);
this.handleError(response, customer);
});
}
private handleError(response: IMIResponse, customer: string): void {
this.logWarning("MI transaction " + response.transaction + " failed");
const errorCode = response.errorCode;
const errorField = response.errorField;
const errorMessage = response.errorMessage;
let message = "Unable to get basic data for customer " + customer;
if (errorCode === "WCU0203") {
message = "The customer " + customer + " does not exist";
}
this.logError(message + " " + errorMessage);
}
List transactions are MI transactions that returns multiple rows. The result is returned as a MIResponse with an items property. These types of transactions requires that maxReturnedRecords is set. The default value is 100.
Always specify the output fields that are required so improve performance. On the response check for erros as the MI program might have replied with an errorCode for the transaction. The errorMessage will always be set if there is an error such as a more generic http error that will result in a call to the errorHandler.
There are two error scenarios that needs to be considered:
import { Component, OnInit, ViewChild } from "@angular/core";
import { CoreBase, IMIRequest, IMIResponse, IUserContext, MIRecord } from "@infor-up/m3-odin";
import { MIService, UserService } from "@infor-up/m3-odin-angular";
import { SohoDataGridComponent } from "ids-enterprise-ng";
@Component({
templateUrl: "./customer.component.html"
})
export class CustomerSampleComponent extends CoreBase implements OnInit {
@ViewChild("customersDatagrid") datagrid: SohoDataGridComponent;
datagridOptions: SohoDataGridOptions;
items: any[] = [];
isBusy = false;
private userContext: IUserContext;
constructor(private miService: MIService, private userService: UserService) {
super("CustomerComponent");
this.initGrid();
}
ngOnInit() {
this.listItems();
}
private initGrid() {
// ...
}
listItems() {
if (this.isBusy) { return; }
this.setBusy(true);
const request: IMIRequest = {
program: "CRS610MI",
transaction: "LstByNumber",
outputFields: ["CUNO", "CUNM", "CUA1", "CUA2"],
maxReturnedRecords: 10,
};
// Start the list with record 1000, example of input
const inputRecord: MIRecord = new MIRecord();
inputRecord.setString("CUNM", "1000");
request.record = inputRecord;
this.miService.execute(request).subscribe((response: IMIResponse) => {
this.setBusy(false);
if (!response.hasError()) {
this.items = response.items;
//
this.updateGridData();
}
}, (error) => {
this.setBusy(false);
// Handle error
});
}
The IonApiService
can be configured with various optional properties and overrides using the IonApiConfig
DI token:
import { IonApiConfig, M3OdinModule } from '@infor-up/m3-odin-angular';
@NgModule({
imports: [
M3OdinModule,
],
providers: [
{
provide: IonApiConfig,
useValue: {
withCredentials: true, // Override XMLHttpRequest.withCredentials. Default is false
},
},
],
})