Total Selected Rows – Jscript to Javascript – H5 Revisited

IFL has been running 13.4 for some months now, and given what’s not being said about Smart Office in a couple of the conferences earlier this year, I felt it was time to investigate migrating scripts to H5.

Previously, I had been reluctant to pursue this because H5 really lacked maturity and for the majority of our users, Smart Office is just easier to use. The last time I did some serious work with it, it felt like whack-a-mole with bugs and functionality. But we’re now more than a year further down the track and it’s clear that there has been a lot of focus put on H5.

It also helps that the H5 SDK document (Infor M3 H5 Development Guide) in the Knowledge Base talks about debugging the Javascripts with Visual Studio and has some templates – something that piqued my interest 🙂

Though I’ve played around with the H5 scripts in the past, and the updated PDF is more than a copy/paste/tweak from the Smart Office SDK – it discusses new methods to work with the UI so I figured I’d start with a Smart Office script that was pretty simple but extracted data from the ListView, added controls, updated controls and had event subscriptions. Also the H5 Development Guide recommends using TypeScript which I’ve never worked with so I wanted to get a bit of a feel for it without dealing with too much change.

The script in question is on MWS060/B. It totals the on hand balance, allocated quantity and provides a count of the selected records in the ListView.

Pretty basic sort of stuff.

We’re going to be adding 6 controls, 3 labels, 3 TextBoxes. We are going to subscribe to the event on the ListView which fires when the selection changes, and we are going to extract data from the selected records on the listview. This example covers most of the fundamentals in my scripts.

First things first. I grabbed the Visual Studio template from KB 1909067 (it’s an attachment). Then I added my new Typescript file, MWS060_TotalSelectedRows to the project.

Note: The .ts file automatically generates a Javascript .js file which is what we need to upload and reference in H5.

I added a few lines of code so I could test the script and breakpoints, but this is where I encountered my first issue.

Before we go in to that, we probably need to do some explaining first – please refer to the documentation for more information as I am only going to touch on some key points.

In the H5ScriptsProjectTemplate we have a start URL, this points to H5 and we provide some additional arguments. We also have a location for a local webserver where our script will actually be loaded from.

My URL is
https://ifldev-bel1.cloud.infor.com:63906/mne/?scriptCache=false&localScript=http://localhost:50167/MWS060_TotalSelectedRows.js

And where the script is loaded from is
http://localhost:50167/

When we run our project, IE will be launched and we log in to the H5 client, we then navigate to the program that has our script (MWS060_TotalSelectedRows.js) attached. The page will attempt to load the script from http://localhost:50167/ however, we are mixing https with http – IE, depending on your security settings will dutifully not load the http content.

What I had encountered was an issue with a mix of content secured from M3 and content that wasn’t secured off my machine. To allow this to work, you need to change a setting on your internet settings (or set the local IIS Express instance to use https – but I couldn’t quickly get that up and running).

I added M3/H5 server to my trusted sites zone, and I set the display mixed content to prompt. Yes, you could change this to enable, but even with trusted sites I’d prefer to know that we have potential issues.

Now I get this:

Click on Show all content -> Leave this page

You’ll be dropped back to the main H5 page, but I can just launch MWS060 again and my breakpoint gets triggered

The next thing to do was to subscribe to the SelectedRowsChanged event, the H5Developers Guide has an example of doing this,

    private run(): void
    {
        this.AddControls();

        const list = this.gController.GetGrid();
        const handler = (e, args) => { this.onSelectionChanged(e, args); };
        list.onSelectedRowsChanged.subscribe(handler);
    }

    private onSelectionChanged(e: any, args: any): void
    {
        let grid = args.grid;
        let selected = grid.getSelectedRows();
    }

Interestingly on my MUA build, the event doesn’t trigger on the first click, you have to select a record and then select a second record after the fact before the event triggers. I’m waiting on a new build to be applied to the MUA before I log an issue.

We need to add our controls to the panel – nothing special there.

Next came the part that I was expecting to be straight forward, the actual updating of my controls with the values. With H5 Infor provide a number of common methods for you to extract and update UI data but this is where what was available departed from what the documentation said. They provide an example of using ScriptUtil to update values, but the arguments in the documentation is very different from what I could use. Eventually I noticed that the controller has an option to SetValue so I can do something like this:

this.gController.SetValue(“tbSelectedOhB”, totalOhB);

Ultimately we end up with this

It’s a little disappointing that I’m still hitting bugs with H5 that impact basic functionality but it’s likely something that I can work around by subscribing to a different event. Despite that, it is feeling a lot more mature and we have the GetMode() method which tells us whether we got to the panel through a create/change/copy/delete/display action – something I have been wanting for years.

There is definitely room for me to refine my development process which I’ll do as I spend more time with it.

Anyway, I’ve attached the Smart Office Jscript and the H5 TypeScript below.

Original JScript:

/*
**	Name: 			MWS060_TotalSelectedRows
**	Program:		MWS060/B
**	Description:	Totals the allocated/on hand balance and number of rows selected
**
**	History
**		20150727	- added on-hand balance
**		20161110	- quantities display two decimals now
*/
import System;
import System.Windows;
import System.Windows.Controls;
import MForms;

import Mango.UI;

package MForms.JScript
{
	class MWS060_TotalSelectedRows
	{
		var gDebug = null;
		var gController = null;
		var gContent = null;

		var giAllocatableColumn : int = -1;
		var gstrAllocateableColumnName : String = "AVAL";
		var giOnHandBalanceColumn : int = -1;
		var gstrOnHandBalanceColumnName : String = "STQT";
		
		var glvListView = null;
		
		var gtbSelectedAllocated : TextBox = new TextBox();
		var glblSelectedAllocated : Label = new Label();
		var gtbSelectedPallets : TextBox = new TextBox();
		var glblSelectedPallets : Label = new Label();
		var gtbSelectedOhB : TextBox = new TextBox();
		var glblSelectedOhB : Label = new Label();
		
		var giStartRow : int = 5;
		var giColumn : int = 50;
		
		var gQuantityFormat : String = "d2";
		
		public function Init(element: Object, args: Object, controller : Object, debug : Object)
		{
			gContent = controller.RenderEngine.Content;
			gController = controller;
			
			gDebug = debug;
			
			var lcListControl : MForms.ListControl = gController.RenderEngine.ListControl;
			glvListView = lcListControl.ListView;
			
			giAllocatableColumn = lcListControl.GetColumnIndexByName(gstrAllocateableColumnName);
			giOnHandBalanceColumn = lcListControl.GetColumnIndexByName(gstrOnHandBalanceColumnName);
			
			if(-1 != giAllocatableColumn)
			{
				glblSelectedAllocated.Content = "Sel Alloc. Qty:";
				Grid.SetRow(glblSelectedAllocated, giStartRow);
				Grid.SetColumn(glblSelectedAllocated, giColumn);
				Grid.SetColumnSpan(glblSelectedAllocated, 10);
				
				Grid.SetRow(gtbSelectedAllocated, giStartRow);
				Grid.SetColumn(gtbSelectedAllocated, giColumn + 10);
				Grid.SetColumnSpan(gtbSelectedAllocated, 10);
				gtbSelectedAllocated.Height = Configuration.ControlHeight;
				gtbSelectedAllocated.Width = 100;
				gtbSelectedAllocated.TextAlignment = TextAlignment.Right;
				gContent.Children.Add(gtbSelectedAllocated);
				gContent.Children.Add(glblSelectedAllocated);
			}

			glblSelectedPallets.Content = "Sel Pallet. Qty:";
			Grid.SetRow(glblSelectedPallets, giStartRow+1);
			Grid.SetColumn(glblSelectedPallets, giColumn);
			Grid.SetColumnSpan(glblSelectedPallets, 10);
			
			Grid.SetRow(gtbSelectedPallets, giStartRow+1);
			Grid.SetColumn(gtbSelectedPallets, giColumn + 10);
			Grid.SetColumnSpan(gtbSelectedPallets, 10);
			gtbSelectedPallets.Height = Configuration.ControlHeight;
			gtbSelectedPallets.Width = 100;
			gtbSelectedPallets.TextAlignment = TextAlignment.Right;
			
			if(-1 != giOnHandBalanceColumn)
			{
				glblSelectedOhB.Content = "Sel On hand Bal:";
				Grid.SetRow(glblSelectedOhB, giStartRow-1);
				Grid.SetColumn(glblSelectedOhB, giColumn);
				Grid.SetColumnSpan(glblSelectedOhB, 10);
				
				Grid.SetRow(gtbSelectedOhB, giStartRow-1);
				Grid.SetColumn(gtbSelectedOhB, giColumn + 10);
				Grid.SetColumnSpan(gtbSelectedOhB, 10);
				gtbSelectedOhB.Height = Configuration.ControlHeight;
				gtbSelectedOhB.Width = 100;
				gtbSelectedOhB.TextAlignment = TextAlignment.Right;		
				gContent.Children.Add(glblSelectedOhB);
				gContent.Children.Add(gtbSelectedOhB);
			}

			gContent.Children.Add(glblSelectedPallets);
			gContent.Children.Add(gtbSelectedPallets);
			
			glvListView.add_SelectionChanged(onSelectionChanged);
			gController.add_Requested(OnRequested);
		}
		
		public function onSelectionChanged(sender : Object,  args : SelectionChangedEventArgs)
		{
			if(-1 != giAllocatableColumn)
			{
				gtbSelectedAllocated.Text = "";
			}
			if(-1 != giOnHandBalanceColumn)
			{
				gtbSelectedOhB.Text = "";
			}
			gtbSelectedPallets.Text = "";
			
			if(null != glvListView.SelectedItem && glvListView.SelectedItems.Count > 0)
			{
				var iAllocatedCount : decimal = 0;
				var iOnHandBalanceCount : decimal = 0;
				for(var row in glvListView.SelectedItems)
				{
					if(-1 != giAllocatableColumn)
					{
						iAllocatedCount += decimal(row[giAllocatableColumn]);
					}
					if(-1 != giOnHandBalanceColumn)
					{
						iOnHandBalanceCount += decimal(row[giOnHandBalanceColumn]);
					}
				}
				if(-1 != giAllocatableColumn)
				{
					gtbSelectedAllocated.Text = iAllocatedCount.ToString(gQuantityFormat);
				}
				
				if(-1 != giOnHandBalanceColumn)
				{
					gtbSelectedOhB.Text = iOnHandBalanceCount.ToString(gQuantityFormat);
				}
				gtbSelectedPallets.Text = glvListView.SelectedItems.Count.ToString();
			}
		}
		
		public function OnRequested(sender: Object, e: RequestEventArgs)
	  	{
			if(MNEProtocol.CommandTypePage != e.CommandType)
			{
				// remove our event subscriptions
				gController.remove_Requested(OnRequested);
				glvListView.add_SelectionChanged(onSelectionChanged);
			}

		}

	}
}

TypeScript (note: this will not work if uploaded directly to M3, it needs to be converted to javascript)

/*
**  Name:   MWS060_TotalSelectedRows
**  Panel:  MWS060/B
**
**  Description:
**      Totals the selected records On hand Balance, Allocatable Balance and counts the records selected
**
**  Written By:
**      scott.campbell@indfish.co.nz
**
**  History:
**      20170823    SAC * Ported to H5
**
*/
class MWS060_TotalSelectedRows
{
    gDebug: IScriptLog = null;
    gController: IInstanceController = null;
    gContent: IContentElement = null;

    gAllocateableColumnName: String = "AVAL";
    gOnHandBalanceColumnName: String = "STQT";

    gListView = null;

    /*
    **  New controls that we will add to the panel
    */
    tbSelectedAllocated: any = new TextBoxElement();
    lbSelectedAllocated: any = new LabelElement();
    tbSelectedPallets: any = new TextBoxElement();
    lbSelectedPallets: any = new LabelElement();
    tbSelectedOhB: any = new TextBoxElement();
    lbSelectedOhB: any = new LabelElement();

    gStartRow = 5;
    gColumn = 50;

    gQuantityFormat = "d2";

    constructor(scriptArgs: IScriptArgs)
    {
        this.gDebug = scriptArgs.log;
        this.gController = scriptArgs.controller;
        this.gContent = scriptArgs.controller.GetContentElement();
        this.gListView = ListControl.ListView;
    }

    private run(): void
    {
        this.AddControls();

        const list = this.gController.GetGrid();
        const handler = (e, args) => { this.onSelectionChanged(e, args); };
        list.onSelectedRowsChanged.subscribe(handler);
    }

    private onSelectionChanged(e: any, args: any): void
    {
        let grid = args.grid;
        let selected = grid.getSelectedRows();

        if (selected.length > 0)
        {
            let totalOhB = 0;
            let totalAllocated = 0;
            let totalPallets = 0;

            let OhB = ListControl.ListView.GetValueByColumnName(this.gOnHandBalanceColumnName);
            let allocated = ListControl.ListView.GetValueByColumnName(this.gAllocateableColumnName);
            

            if (null != OhB)
            {
                for (let currentOhB of OhB)
                {
                    totalOhB += +currentOhB;
                }
                for (let currentAllocated of allocated)
                {
                    totalAllocated += +currentAllocated;
                }

                this.gController.SetValue("tbSelectedPallets", allocated.length);
                this.gController.SetValue("tbSelectedOhB", totalOhB);
                this.gController.SetValue("tbSelectedAllocated", totalAllocated);
            }
        }
    }

    private AddControls()
    {
        if (null != this.gListView.GetValueByColumnName(this.gAllocateableColumnName))
        {
            this.lbSelectedAllocated.Position = new PositionElement();
            this.lbSelectedAllocated.Position.Top = this.gStartRow;
            this.lbSelectedAllocated.Position.Left = this.gColumn;
            this.lbSelectedAllocated.Value = "Sel Alloc. Qty:";
            this.gContent.AddElement(this.lbSelectedAllocated);

            this.tbSelectedAllocated.Position = new PositionElement();
            this.tbSelectedAllocated.Position.Top = this.gStartRow;
            this.tbSelectedAllocated.Position.Left = (this.gColumn + 10);
            this.tbSelectedAllocated.Position.Width = 10;
            this.tbSelectedAllocated.IsRightJustified = true;
            this.tbSelectedAllocated.Name = "tbSelectedAllocated";

            this.gContent.AddElement(this.tbSelectedAllocated);
        }

        this.lbSelectedPallets.Position = new PositionElement();
        this.lbSelectedPallets.Position.Top = this.gStartRow - 1;
        this.lbSelectedPallets.Position.Left = this.gColumn;
        this.lbSelectedPallets.Value = "Sel Pallet. Qty:";
        this.gContent.AddElement(this.lbSelectedPallets);

        this.tbSelectedPallets.Position = new PositionElement();
        this.tbSelectedPallets.Position.Top = this.gStartRow - 1;
        this.tbSelectedPallets.Position.Left = (this.gColumn + 10);
        this.tbSelectedPallets.Position.Width = 10;
        this.tbSelectedPallets.IsRightJustified = true;
        this.tbSelectedPallets.Name = "tbSelectedPallets";

        this.gContent.AddElement(this.tbSelectedPallets);

        if (null != this.gListView.GetValueByColumnName(this.gOnHandBalanceColumnName))
        {
            this.lbSelectedOhB.Position = new PositionElement();
            this.lbSelectedOhB.Position.Top = this.gStartRow + 1;
            this.lbSelectedOhB.Position.Left = this.gColumn;
            this.lbSelectedOhB.Value = "Sel Pallet. Qty:";
            this.gContent.AddElement(this.lbSelectedOhB);

            this.tbSelectedOhB.Position = new PositionElement();
            this.tbSelectedOhB.Position.Top = this.gStartRow + 1;
            this.tbSelectedOhB.Position.Left = (this.gColumn + 10);
            this.tbSelectedOhB.Position.Width = 10;
            this.tbSelectedOhB.Name = "tbSelectedOhB";
            this.tbSelectedOhB.IsRightJustified = true;

            this.gContent.AddElement(this.tbSelectedOhB);
        }
    }

    public static Init(args: IScriptArgs): void
    {
        new MWS060_TotalSelectedRows(args).run();
    }
}
This entry was posted in Development, H5 Client, M3 / MoveX. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s