JavaScript Extensibility was made available in Simphony 19.6 and has been further enhanced in Simphony 19.7. JavaScript Extensibility improved on SIM/C# with the following characteristics:
C# and SIM Extensibility is only available for OPS. There is a C# plugin support for Transaction Services but it is limited.
JavaScript Extensibility applications can be written for Transaction Services as well as OPS. The goals of Transaction Services JavaScript Extensibility are:
Unfortunately, the operating environment dictates a different JavaScript extensibility model. This table attempts to summarize these differences
OPS | Transaction Services | |
---|---|---|
Root engine | Performs all transaction business logic | Minimal, used to configure service script and possibly read common configuration. In general, we say that the Transaction Services root script is “administrative”, it is not intended to contain any transaction business logic. |
Service Engine | Not applicable. | Performs all transaction business logic. |
Lifetime | The root script lifetime exists for the lifetime of servicehost.exe. Once it is created it is never destroyed until service host is shut down OR the extension application is updated |
The root script lifetime exists for the lifetime of servicehost.exe.
The service engine exists for the lifetime of the Transaction Services request. That is, a new service engine is created when the Transaction Services request begins and is destroyed when the Transaction Services request ends. A single service script can subscribe to multiple events and keep persistent data, but only for the lifetime of the Transaction Services request. There can be multiple service engines active at a single time, each with its own address space. |
Datastore | Extensibility Datastore |
Extensibility Datastore |
Context | standard OpsContext |
A limited “BaseContext”. The TS context contains simple configuration
information such as WorkstationID. Please see Context (any
Host-Type) in the SimphonyExtensibilityAPI section. |
Events | standard OPS extensibility events available to SIM and C# |
A limited set of events. Please see Events available for Transaction
Services in the POS Events section |
Transaction | SIM/C#/JavaScript retain the classic interactive event model for transactions: BeginCheck, MenuItem, MenuItem, FinalTender, … |
Transaction Services requests are “batch” oriented. There is no concept
of BeginCheck, MenuItem confirm, etc. The entire check is presented to
the Transaction Services service engine and is processed in one event.
|
OPS ordering is interactive while Transaction Services ordering is batch oriented.
To support this a new Transaction Services event API was created (SubmitCheck). This SubmitCheck API was then made available to OPS. Thus, the same code could be reused in both host types.
OPS is inherently single threaded. Only one check can be active a time. The OPS root script is always present. It is only destroyed when the ext app has changed or service host shuts down.
Transaction Services fields multiple simultaneous requests. Therefore, multiple active Transaction Services Service JavaScript engines need to be created, one for each Transaction Services request. Each Transaction Services Service JavaScript engine has its own address space; the Transaction Services engines can only share data through the API ApplicationObjects map.
The Transaction Services root script defines the Transaction Services engine scripts, but Transaction Services will use this configuration to instantiate a Transaction Services engine for each request.
A new set of events was created for Transaction Services service engines. They are:
In addition, the following events exist, similar to the OPS events:
The following diagram attempts to show how events are processed for each Transaction Services request.
Contrast this with how OPS processing multiple checks in sequence.
All JavaScript extensibility applications are provided a “Context” object. It is accessed with SimphonyExtensibilityAPI.Environment.Context. However, the underlying Context object is not the same, based on the host type.
BaseHostContext contains a minimal set of configuration parameters. It is notably less featured than OpsContext, as much of what OpsContext provides would not be valid in Transaction Services. (dialog requests, current check, etc).
Method / Property | Description |
---|---|
string MapRootPath( string fileName ) |
Get application root path based on file-name provided |
string PropertyNumber |
Property number as configured in EMC |
long PropHierStrucID |
internal Hierarchy Structure ID for this Property |
int PropertyID |
internal Property ID |
string PropertyName |
Property name as configured in EMC |
string WorkstationName |
Workstation name as configured in EMC |
int WorkstationNumber |
Workstation number as configured in EMC |
int WorkstationID |
internal Workstation ID |
string HostName |
Host-name of the device |
int OrganizationId |
internal Organization ID |
int LocationId |
internal Location ID |
int CurrentLangId |
internal, current Language ID |
int CurrentLangObjNum |
current Language object number |
Networking Networking |
Networking object |
int ServiceHostID |
internal ServiceHost ID |
string EGatewayURL |
URL of the EGateway resource |
string Version |
Version string of Simphony |
string IPaddress |
IP address of the device |
ApiOperatingSystem OperatingSystem |
see ApiOperatingSystem members below |
Method / Property | Description |
---|---|
bool IsWin32 |
True when running under Windows |
bool IsNotWin32 |
True for both Android and Linux |
bool IsAndroid |
True when running under Android |
bool IsLinux |
True when running under Linux |
Multiple Extensibility applications can be configured and executed in a single ServiceHost OPS/Transaction Services workstation.
Just as with OPS, multiple Extensibility applications can subscribe
to the SubmitCheck
event, each which can abort the event.
The SubmitCheck
event model was designed to handle multiple
Extensibility applications, and extensibility writers should take care
when writing code.
Consider 3 extension applications configured: JS-X, JS-Y, JS-Z. Each
subscribes to the SubmitCheck
events, each can abort the
SubmitCheck
event.
Notes:
SubmitCheckRejected
event will be run for ext apps
that processed the SubmitCheck
event. In the drawing above,
if JS-Y aborted the SubmitCheck
, only JS-X will get the
rejected event. JS-Z will not as it never ran the
SubmitCheck
event.SubmitCheckNotification
event will be run for all
ext apps, whether the check was accepted or rejected.Property | Description |
---|---|
CheckBase Check |
Check header and detail. Check.Header: same as OPS OpsContext.Check Check.Detail: same as OPS OpsContext.CheckDetail |
string RejectedMessage |
Ext. Apps can set this if they abort a SubmitCheck event. |
Property | Description |
---|---|
string RejectedMessage |
Message optionally set by extension application that aborted the event |
string ExtAppThatAborted |
The name of the extension application that aborted the event |
IList<string> ExtAppsThatExecuted |
List of application that processed the SubmitCheck event |
bool AbortedByExtApp |
true if an ext app aborted the event, false if an error occurred within transaction services core code |
Property | Description |
---|---|
bool Success |
true if SubmitCheck completed successfully |
string RejectedMessage |
see SubmitCheckRejectedEventArgs |
string ExtAppThatAborted |
see SubmitCheckRejectedEventArgs |
IList<string> ExtAppsThatExecuted |
see SubmitCheckRejectedEventArgs |
bool AbortedByExtApp |
see SubmitCheckRejectedEventArgs |
The sample code:
Notes on code:
import * as _lib from "lib";
.configureSubmitCheck(); _lib
import * as _lib from "lib";
var _api = SimphonyExtensibilityAPI;
.Eventing.SubscribeToEvent('InitEvent', OnInitEvent);
_api
//--------------------------------------------------------------------------
function OnInitEvent(sender, args)
{// configure the TS service script
let parms = _api.Runtime.AllocateServiceParameters();
.ContentName = 'ts-service';
parms.Runtime.SetService(parms);
_apireturn _api.Eventing.Continue;
}
import * as _lib from "lib";
.configureSubmitCheck(); _lib
var _api = SimphonyExtensibilityAPI;
var _isOPS = _api.Runtime.HostType == _api.Common.HostType.OPS;
//--------------------------------------------------------------------------
export function configureSubmitCheck()
{log('configureSubmitCheck');
// subscribe to transaction events
.Eventing.SubscribeToEvent('SubmitCheckEvent', OnSubmitCheckEvent);
_api.Eventing.SubscribeToEvent('SubmitCheckRejectedEvent', OnSubmitCheckRejectedEvent);
_api.Eventing.SubscribeToEvent('SubmitCheckNotificationEvent', OnSubmitCheckNotificationEvent);
_api.Eventing.SubscribeToEvent('OpsCustomReceiptEvent', OnOpsCustomReceiptEvent);
_api
// lifetime events.
// these are not as useful in OPS. These would most likely be subscribed to by
// TS service scripts and handled differently in OPS.
var prefix = _isOPS ? "Ops" : "";
.Eventing.SubscribeToEvent(prefix + 'InitEvent', OnInitEvent);
_api.Eventing.SubscribeToEvent(prefix + 'ExitEvent', OnExitEvent);
_api
}
//--------------------------------------------------------------------------
function OnInitEvent(sender, args)
{log('OnInitEvent');
}
function OnExitEvent(sender, args)
{log('OnExitEvent');
}
//--------------------------------------------------------------------------
function OnSubmitCheckEvent(sender, args)
{log('OnSubmitCheckEvent');
// allocate text extensibility info
let textExtInfo = _api.Common.AllocateExtensibilityDataInfo(`${_api.Runtime.ApplicationName} check text ext dtl`, "ExtDataName-Header", "some data for header");
.SetPrintOnDisplayOnly();
textExtInfo.VisibleToTransactionServices = true;
textExtInfo
// add to check header
let header = args.Check.Header;
.AddExtensibilityData(textExtInfo);
header
// iterate through check detail, add ext data to all menu items
let detail = args.Check.Detail.Scriptable;
for (let i = 0; i < detail.Count; i++)
{let dtlItem = detail[i];
if (dtlItem.DetailType == _api.Common.CheckDetailType.DtlTypeMi)
{let dtlTextExtInfo = _api.Common.AllocateExtensibilityDataInfo(`${_api.Runtime.ApplicationName} mi dtl ext dtl`, "ExtDataName-MiDtl", "some data for mi dtl [" + (i+1) + "]");
.VisibleToTransactionServices = true;
dtlTextExtInfo.AddExtensibilityData(dtlTextExtInfo);
dtlItem
}
}
// now we need to determine whether to allow/cancel the check.
// ops will ask the user whether to cancel
// if ops ask a question, otherwise reject if cover count > 1
let acceptCheck = false;
if (_isOPS)
= _api.Environment.Context.AskQuestion(`${_api.Runtime.ApplicationName}: SubmitCheck: continue?`);
acceptCheck else
= (header.Covers <= 1);
acceptCheck
if (acceptCheck)
{return _api.Eventing.Continue;
}else
{.RejectedMessage = `${_api.Runtime.ApplicationName}: rejected check`;
argsreturn _api.Eventing.AbortEvent;
}
}
//--------------------------------------------------------------------------
function OnSubmitCheckRejectedEvent(sender, args)
{log('OnSubmitCheckRejectedEvent');
return _api.Eventing.Continue;
}
//--------------------------------------------------------------------------
function OnSubmitCheckNotificationEvent(sender, args)
{log('OnSubmitCheckNotificationEvent');
// format for dialog
if (_isOPS)
{let msg = formatRejectedData(args, '\n');
.Environment.Context.ShowMessage(`${_api.Runtime.ApplicationName}: ${msg}`);
_api
}
// format for log file
log(formatRejectedData(args, ''));
return _api.Eventing.Continue;
}
//--------------------------------------------------------------------------
function formatRejectedData(args, separator)
{let line = `Event: [${args.EventID}] ${separator}`;
+= `Success [${args.Success}] ${separator}`;
line += `RejectedMessage [${args.RejectedMessage}] ${separator}`;
line += `ExtAppThatAborted [${args.ExtAppThatAborted}] ${separator}`;
line += `AbortedByExtApp [${args.AbortedByExtApp}] ${separator}`;
line += `ExtAppsThatExecuted:${separator} `;
line for (let i = 0; i < args.ExtAppsThatExecuted.Count; i++)
+= `[${args.ExtAppsThatExecuted[i]}] ${separator}`;
line return line;
}
//------------------------------------------------------------------
function OnOpsCustomReceiptEvent(sender, args)
{inform(args.EventID);
}
//------------------------------------------------------------------
export function inform(msg)
{if (_isOPS)
.Environment.Context.ShowMessage(`${_api.Runtime.ApplicationName}: ${msg}`);
_apilog(msg);
}
//------------------------------------------------------------------
function log(msg)
{console.log(`${_api.Logger.Prefix}; ${msg}`);
.Logger.LogAlways(msg);
_api }