Simphony JavaScript Extensibility

JavaScript Extensibility details

Iterating Through Check Detail

Introduction

Reading OpsContext check header/detail is a common operation in extensibility applications. Since the JavaScript API makes OpsContext available to the JS extension application, the ext app can iterate through the check detail in the same way as a C# program would.

Reading check detail is made slightly more complicated for the following reasons.

  • The JS ext app is accessing a C# object, not a native JavaScript object. This means that normal JavaScript array practices (such as array forEach()) do not work on C# CheckDetail arrays..
  • All C# properties/methods are available to the JS ext app. C# language-specific constructs such as “foreach” are not available to the JavaScript application.
  • The JavaScript engine has difficulty with the [] operator on certain types of C# collections. JavaScript, depending on the referenced object, uses the JavaScript [] operator and not the C# indexer. For OpsContext.CheckDetail this can be solved with the ItemAt() function.
  • The OpsContext API was written to be access by C# applications. The JavaScript engine allows for access to the API, it just isn’t always as clean as C#.
  • While modifications could be made to the OpsContext.CheckDetail API to make it more JavaScript-friendly, this would break the API for all existing applications. The “Scripting” member was added to the CheckDetail collections, it is a reasonable addition to help with scripting.

C# versus Javascript Iteration

Consider the following C# examples:

foreach ( var dtl in opsContext.CheckDetail )
{
}

for ( int i = 0; i < opsContext.CheckDetail.Count; i++ )
{
 var dtl = opsContext.CheckDetail.ItemAt( i );
}

Both of these C# code samples iterate over the top-level CheckDetail entries.

// forEach() does NOT work here. CheckDetail is a C# collection, not
// a JavaScript Array
opsContext.CheckDetail.forEach((dtl) => console.log(dtl));

// standard iteration pattern works
for (let i = 0; i < opsContext.CheckDetail.Count; i++) {
 let dtl = checkDetail.ItemAt(i);
}

In these 2 JavaScript samples, only the 2nd iteration method is valid.

Scriptable

The detail lists in OpsContext.CheckDetail are a “ExtensibilityDetailArray” object. This class implements a few collection-like methods: Count, item indexer, GetEnumerator(), but the container itself is not as flexible as the IList<> container. The Scriptable property on each detail container provides an IList<> implementation to aid with iteration.

The following properties are ExtensibilityDetailArray objects on the following classes:

  • ReferenceEntries
  • ExtensibilityData
  • Condiments
  • ComboSides
  • Fees
  • ExtensibilityData

The following Javascript examples show two ways to iterate over check detail, the native way and using Scriptable.

// native
for (let i = 0; i < checkDetail.Count; i++) {
 let dtl = checkDetail.ItemAt(i);
}

// Scriptable
for (let i = 0; i < checkDetail.Scriptable.Count; i++) {
 let dtl = checkDetail.Scriptable[i];
}

JavaScript engine and Upcasting

Consider the following Javascript code sample.

for (let i = 0; i < checkDetail.Count; i++) {
 // dtl is a CheckDetailItem reference
 let dtl = checkDetail.ItemAt(i);
}

“dtl” is a CheckDetailItem reference. Even though it may be a MenuItemDetail or DiscountDetail object, the only members available on “dtl” are ones defined on CheckDetailItem.

If one wants to access MenuItemDetail members, the script write must either cast “dtl” to a MenuItemDetail or a C# object. This is JavaScript engine behavior, not OPS behavior.

Casting to an Object has been made trivial in Simphony 19.7.

var _api = SimphonyExtensibilityAPI;   
for (let i = 0; i < checkDetail.Count; i++) {
 // dtl is a CheckDetailItem reference
 let dtl = checkDetail.ItemAt(i);

 // dtl is now an object reference, and all members are available to scripting,
 // even from dervied types.
 dtl = _api.Common.ToObject(dtl);
}

Since the Scriptable member returns an IList <object> this casting is not necessary when using Scriptable.

for (let i = 0; i < checkDetail.Scriptable.Count; i++) {
 // dtl is an object reference via Scriptable
 let dtl = checkDetail.Scriptable[i];
}

Should you use Scriptable or the native iteration method? Both patterns are equivalent, it is purely a stylistic choice.

More Complete Samples

The following samples show how to iterate over CheckDetail and the ComboSides array within the menu item.

// iterate over native list.
for (let i = 0; i < checkDetail.Count; i++) {
 let dtl = checkDetail.ItemAt(i);
 dtl = _api.Common.ToObject(dtl);

 if (dtl.DetailType == _api.Common.CheckDetailType.DtlTypeMi) {
     for (let j = 0; j < dtl.ComboSides.Count; j++) {
         let childDtl = dtl.ComboSides.ItemAt(j);
         childDtl = _api.Common.ToObject(childDtl);
     }
 }
}

// iterate over Scriptable
for (let i = 0; i < checkDetail.Scriptable.Count; i++) {
 let dtl = checkDetail.Scriptable[i];

 if (dtl.DetailType == _api.Common.CheckDetailType.DtlTypeMi)
 {
     for (let j = 0; j < dtl.ComboSides.Scriptable.Count; j++) {
         let childDtl = dtl.ComboSides.Scriptable[j];
     }
 }
}

JS Exception Logging

When the JavaScript engine encounters a fatal error in parsing a script, it will throw an exception back to ServiceHost. This exception provides module/line/column information about the script error.

Previous versions of Simphony would not display this detailed information. ServiceHost will now display/log the detailed script error details to help the developer narrow down the syntax issue.

Consider the following script:

TS Event Model

Line 7 is not valid javascript.

For all host types (OPS, TS, ES) the error will be logged to the egateway log. For OPS, an error window will be raised.

The following error is logged:

05/21/25 14:32:19.472, 0, 0,Unknown     ,       10,===    exception encountered: Micros.PosCore.Extensibility.ExtensibilityException: JS Engine Exception
Ext App: DateTimeTest
Host Type: OPS
Script Name: js
Engine Type: Root
Engine ID: #17
SyntaxError: Invalid or unexpected token
at Module [temp]:7:3 -> a # c;

The following dialog is displayed in OPS:

TS Event Model

The information logged identifies the context (ext app name, script name, etc) and the syntax error (message, module, line, …)

In this case the “a # c” syntax occurred on line 7, column 3.

Since this is a root script, the module name is [temp].