Modification Removal – MMS100 – Barcoding

This is another post around the removal of modifications.

This post really discusses the changes to a panel to streamline it for some very specific uses, includes error monitoring, supressing the F4 browse, hiding, moving, and adding fields.

The Problem

Let us go back to the 2003/2004 timeframe…as part of our ERP project, we wanted to streamline our stock in and out processes. At that point in time our freezer staff would have sheets of paper which they would use to determine the product going on to an order and they would manually write where they retrieved the product from and the quantity. Then that sheet of paper would go to another person who would key in the stock movement.

We wanted to put computers on to our forklifts and have the forklift staff scan locations and scan pallets – reducing the paper and potential keying errors.

So, each location in our freezers has a barcode label with the freezer location. And each pallet gets a label which has four pieces of information on them separated by a horizontal tab. So a pallet barcode will go <Item Code>HT<Manufacturing Order Number>HT<Lot Number>HT<Quantity> on the single barcode.

This required us to have any screen that they go in to have four fields in that specific tab order. Not to mention we needed to make it easy for our forklift staff to navigate MoveX Explorer on 800×400 screens.

The Solution with MoveX Explorer

Due to time constraints and a lack of information available on the MoveX APIs, we got forced down the route of modify quite around a dozen panels. Not very kewl, but it largely worked. In this instance we modified MMS100 to look like this:

The Solution with Lawson Smart Office

As mentioned in the past, you can move controls around, resize them, hide them and add them.

Now we uplifted our mods as is in our upgrade to save time and retain staff focus – so even though there was opportunity to change, we err’ed on the side of minimal change.

So mods get uplifted and recently we had a problem with some Distribution Orders (MMS100/MMS101) – problems that could be fixed with fixes we could retrieve through Life Cycle Manager. We put the fix in, we break the mod, we don’t put the fix in we have a broken process, uplifting the mod, well – expensive.

If we look at MMS100/N we see we have all the fields that we require – well – the Lot Number doesn’t appear initially which we can work around that. And we will need to add a field for the MO, but the field is purely a place holder.

The first things first, we need the Lot Number. If we enter some basic information in to the panel and then press enter and…

Presumably if the product is lot controlled the Lot Number field will appear. You’ll also notice that we have a “Confirm” that appeared, so we need to have a second enter to confirm the transaction.

So, we know we have a couple of challenges – handling the Lot Number, adding a new textbox and finally handling the need for a ‘Confirm’.

We will use the screen personalisations to hide the fields that we don’t care about – Tools -> Personalize -> Show/Hide Fields…

Which leaves us with:

Then we use our script to move fields around, add the MO and then we make the Lot Number TextBox and Label visible and Enabled

            // retrieve the controls that we need to move or manipulate
            var tbBano = ScriptUtil.FindChild(content, "WRBANO");
            var lbBanoLabel = ScriptUtil.FindChild(content, "WBA0415");
            var tbOrderType = ScriptUtil.FindChild(content, "WGTRTP");

            var tbWarehouse = ScriptUtil.FindChild(content, "WGWHLO");
            var tbOrderType = ScriptUtil.FindChild(content, "WGTRTP");
            var tbToLocation = ScriptUtil.FindChild(content, "WRWHSL");
            var tbTransactionQuantity = ScriptUtil.FindChild(content, "WRTRQT");
            var tbItemNumber = ScriptUtil.FindChild(content, "WRITNO");

            var lbItemNumber = ScriptUtil.FindChild(content, "WIT0115");

            var lbToWarehouse = ScriptUtil.FindChild(content, "WWH0115");
            var lbTransactionQty = ScriptUtil.FindChild(content, "WTR0115");

            var lbToLocation = ScriptUtil.FindChild(content, "WWS0115");

            // Set the defaults
            tbOrderType.Text = "PRF";
            tbWarehouse.Text = "WSN";

            // we do this so that we can move the focus if this control gets focus
            // (which it does by default when the panel opens)
            tbOrderType.add_GotFocus(OntbOrderTypeGotFocus);

            // we need to add a textbox for the Manufacturing Order number
            gtbManufacturingOrder = new TextBox();
            var lbManufacturingOrder = new Label();
            
            // do the repositioning of the controls
            if(null != tbWarehouse)
            {
                Grid.SetColumn(tbWarehouse, 15);
                Grid.SetRow(tbWarehouse, 2);
                tbWarehouse.TabIndex = 100;
                Grid.SetColumn(lbToWarehouse, 1);
                Grid.SetRow(lbToWarehouse, 2);
            }

            if(null != tbItemNumber)
            {
                Grid.SetColumn(tbItemNumber, 15);
                Grid.SetRow(tbItemNumber, 3);
                tbItemNumber.TabIndex = 1;
                Grid.SetColumn(lbItemNumber, 1);
                Grid.SetRow(lbItemNumber, 3);
            }
            else
            {
                MessageBox.Show("No tbItemNumber");
            }

            if(null != gtbManufacturingOrder)
            {
                lbManufacturingOrder.Content = "M/O Number:";
                Grid.SetColumn(lbManufacturingOrder, 2);
                Grid.SetRow(lbManufacturingOrder, 4);
                content.Children.Add(lbManufacturingOrder);

                Grid.SetColumnSpan(gtbManufacturingOrder, 16);
                Grid.SetColumn(gtbManufacturingOrder, 15);
                Grid.SetRow(gtbManufacturingOrder, 4);
                gtbManufacturingOrder.Name = "MO";
                gtbManufacturingOrder.TabIndex = 2;
                gtbManufacturingOrder.Margin = new Thickness(3,0,0,0);
                content.Children.Add(gtbManufacturingOrder);
                // when we unload this control, we want to know - this way we can clean up our events
                gtbManufacturingOrder.add_Unloaded(OngtbManufacturingOrderUnloaded);
            }

            if(null != tbBano)
            {
                Grid.SetColumn(tbBano, 15);
                Grid.SetRow(tbBano, 5);

                tbBano.Visibility = Visibility.Visible;
                tbBano.IsEnabled = true;

                tbBano.TabIndex = 3;

                lbBanoLabel.Visibility = Visibility.Visible;
                lbBanoLabel.IsEnabled = true;

                Grid.SetColumn(lbBanoLabel, 1);
                Grid.SetRow(lbBanoLabel, 5);
            }
            else
            {
                MessageBox.Show("No tbBano");
            }

            if(null != tbTransactionQuantity)
            {
                Grid.SetColumn(tbTransactionQuantity, 15);
                Grid.SetRow(tbTransactionQuantity, 6);
                tbTransactionQuantity.TabIndex = 4;
                Grid.SetColumn(lbTransactionQty, 1);
                Grid.SetRow(lbTransactionQty, 6);
            }
            else
            {
                MessageBox.Show("No tbTransactionQuantity");
            }

            if(null != tbToLocation)
            {
                Grid.SetColumn(tbToLocation, 15);
                Grid.SetRow(tbToLocation, 7);
                tbToLocation.TabIndex = 5;
                Grid.SetColumn(lbToLocation, 1);
                Grid.SetRow(lbToLocation, 7);
            }
            else
            {
                MessageBox.Show("No tbToLocation");
            }

The next problem is that when we enter MMS100/N the focus is in the Order Type field, but we want it in the Item Number (we pre-populate the Order Type and Warehouse fields).

So we cheat ;-) When the Order Type TextBox gets the focus we set the focus to the Item Number

        public function OntbOrderTypeGotFocus(sender: Object, e: RoutedEventArgs)
        {
            if(null != gContent)
            {
                var tbItemNumber = ScriptUtil.FindChild(gContent, "WRITNO");
                if(null != tbItemNumber)
                {
                    // if this control gets focus, move the focus to the item number textbox
                    tbItemNumber.Focus();
                }
            }
        }

When we hit enter, our lot number field gets cleared :-( so we need to repopulate it, we do this by monitoring the Requested() storing the lot number and then repopulating it on the RequestCompleted() event. We also want to make sure that we only hit enter when we have the Confirm menu AND we have a value in the Item Number field. As discussed in a previous post (http://potatoit.wordpress.com/2012/01/16/extracting-the-error-message/)we will extract the ‘error’ message, we do that by inspecting the controller.Runtime.Result for the Confirm message.

        public function OnRequestCompleted(sender: Object, e: RequestEventArgs)
        {
            gdebug.WriteLine("OnRequestCompleted() Event Command Type: " + e.CommandType);
            gdebug.WriteLine("OnRequestCompleted() Event Command Value: " + e.CommandValue);
            gdebug.WriteLine("OnRequestCompleted() gLotNumber: " + gLotNumber);

            // verify that these are the events we are looking for!
            if(e.CommandType == MNEProtocol.CommandTypeKey)
            {
                if(e.CommandValue == MNEProtocol.KeyEnter)
                {
                    gdebug.WriteLine("OnRequestCompleted() gLotNumber: " + gLotNumber);
                    var tbBano = ScriptUtil.FindChild(gContent, "WRBANO");
                    var tbITNO = ScriptUtil.FindChild(gContent, "WRITNO");

                    // we will only set the lot number if we have an item in
                    // the Item TextBox, this way we don't
                    // accidently populate the lot number if we are getting
                    // the screen refresh after a committed transaction
                    if(false == String.IsNullOrEmpty(tbITNO.Text))
                    {
                        if(null != gController.Runtime.Result)
                        {
                            if(-1 != gController.Runtime.Result.IndexOf("<Msg>Confirm</Msg>"))
                            {
                                // repopulate the lot number
                                tbBano.Text = gLotNumber;
                                // press enter to commit the transaction
                                gController.PressKey(MNEProtocol.KeyEnter);
                            }
                        }
                    }
                    else
                    {
                        // reset the lot number back to null
                        gLotNumber = null;
                    }
                }
            }
        }

We also need to supress users pressing F4 (as all the information should be on barcodes)

        public function OnRequesting(sender: Object, e: CancelRequestEventArgs)
        {
            if(e.CommandType == MNEProtocol.CommandTypeKey)
            {
                // suppress the F4
                if(e.CommandValue == MNEProtocol.KeyF4)
                {
                    e.Cancel = true;
                }
            }
        }

Now lets put it all together

// Script Description:
//  This script is used for the forklifts, and will only run on MMS100/N
//  - will change the positions of various controls on panel MMS100/N
//  - will add a new textbox for the manufacturing order number (even though it isn't used, but it will be on the barcode labels
//  - will change the tab order
//  - will automatically press enter a second time to confirm the transaction
//  - will make the Lot Number field visible
// Prerequisits:
//  - Textboxes not required should be hidden!
//			<Scripts>
//				<Script progid="MMS100_BarcodeView_V001" argument="" target="" />
//			</Scripts>
//			<HiddenFields>
//				<HiddenField name="WRE0115" />
//				<HiddenField name="WRO1515" />
//				<HiddenField name="WTW0415" />
//				<HiddenField name="WDE0415" />
//				<HiddenField name="WTW0215" />
//				<HiddenField name="LBL_L57T5" />
//				<HiddenField name="WSTA115" />
//				<HiddenField name="WRSCD15" />
//				<HiddenField name="WGS0415" />
//				<HiddenField name="WTOF115" />
//				<HiddenField name="WGS0215" />
//				<HiddenField name="WGS0315" />
//				<HiddenField name="WRE2015" />
//				<HiddenField name="WPR0915" />
//				<HiddenField name="WGRESP" />
//				<HiddenField name="WGRORN" />
//				<HiddenField name="WGRORL" />
//				<HiddenField name="WGRORX" />
//				<HiddenField name="WGTWSL" />
//				<HiddenField name="WGDEPT" />
//				<HiddenField name="WGTWLO" />
//				<HiddenField name="WGTRDY" />
//				<HiddenField name="WRRSCD" />
//				<HiddenField name="WGGSR3" />
//				<HiddenField name="WGTOFP" />
//				<HiddenField name="WGGSR1" />
//				<HiddenField name="WGGSR2" />
//				<HiddenField name="WGREMK" />
//				<HiddenField name="WRSTAS" />
//				<HiddenField name="WGPRIO" />
//			</HiddenFields>
//          <TabOrder>WRBANO=102,WGTRTP=2147483647,WGWHLO=2147483647,WRITNO=101,WRTRQT=103,WRALUN=2147483647,WRWHSL=104,</TabOrder>

// 20120112 V001    * Initial Script Finished
//          V009    * Focus will go to the Item Number field if the OrderType field gets selected
// 20120113 V010    * removed the F4 functionality (it doesn't work for the lots anyway and the forklifts should never use lookups)
// 20120116 V011    * if incorrect data is added (eg. ITNO) when we loop attempting to submit the data

import System;
import System.Windows;
import System.Windows.Controls;
import MForms;

package MForms.JScript
{
    class MMS100_BarcodeView_V011
    {
        var gtbManufacturingOrder : TextBox = null;
        
        var gController = null;
        var gdebug = null;
        var gContent = null;

        var gLotNumber;

        public function Init(element: Object, args: Object, controller : Object, debug : Object)
        {
            var content : Object = controller.RenderEngine.Content;

            gController = controller;
            gdebug = debug;
            gContent = content;

            // retrieve the controls that we need to move or manipulate
            var tbBano = ScriptUtil.FindChild(content, "WRBANO");
            var lbBanoLabel = ScriptUtil.FindChild(content, "WBA0415");
            var tbOrderType = ScriptUtil.FindChild(content, "WGTRTP");

            var tbWarehouse = ScriptUtil.FindChild(content, "WGWHLO");
            var tbOrderType = ScriptUtil.FindChild(content, "WGTRTP");
            var tbToLocation = ScriptUtil.FindChild(content, "WRWHSL");
            var tbTransactionQuantity = ScriptUtil.FindChild(content, "WRTRQT");
            var tbItemNumber = ScriptUtil.FindChild(content, "WRITNO");

            var lbItemNumber = ScriptUtil.FindChild(content, "WIT0115");

            var lbToWarehouse = ScriptUtil.FindChild(content, "WWH0115");
            var lbTransactionQty = ScriptUtil.FindChild(content, "WTR0115");

            var lbToLocation = ScriptUtil.FindChild(content, "WWS0115");

            // Set the defaults
            tbOrderType.Text = "PRF";
            tbWarehouse.Text = "WSN";

            // we do this so that we can move the focus if this control gets focus
            // (which it does by default when the panel opens)
            tbOrderType.add_GotFocus(OntbOrderTypeGotFocus);

            // we need to add a textbox for the Manufacturing Order number
            gtbManufacturingOrder = new TextBox();
            var lbManufacturingOrder = new Label();
            
            // do the repositioning of the controls
            if(null != tbWarehouse)
            {
                Grid.SetColumn(tbWarehouse, 15);
                Grid.SetRow(tbWarehouse, 2);
                tbWarehouse.TabIndex = 100;
                Grid.SetColumn(lbToWarehouse, 1);
                Grid.SetRow(lbToWarehouse, 2);
            }

            if(null != tbItemNumber)
            {
                Grid.SetColumn(tbItemNumber, 15);
                Grid.SetRow(tbItemNumber, 3);
                tbItemNumber.TabIndex = 1;
                Grid.SetColumn(lbItemNumber, 1);
                Grid.SetRow(lbItemNumber, 3);
            }
            else
            {
                MessageBox.Show("No tbItemNumber");
            }

            if(null != gtbManufacturingOrder)
            {
                lbManufacturingOrder.Content = "M/O Number:";
                Grid.SetColumn(lbManufacturingOrder, 2);
                Grid.SetRow(lbManufacturingOrder, 4);
                content.Children.Add(lbManufacturingOrder);

                Grid.SetColumnSpan(gtbManufacturingOrder, 16);
                Grid.SetColumn(gtbManufacturingOrder, 15);
                Grid.SetRow(gtbManufacturingOrder, 4);
                gtbManufacturingOrder.Name = "MO";
                gtbManufacturingOrder.TabIndex = 2;
                gtbManufacturingOrder.Margin = new Thickness(3,0,0,0);
                content.Children.Add(gtbManufacturingOrder);
                // when we unload this control, we want to know - this way we can clean up our events
                gtbManufacturingOrder.add_Unloaded(OngtbManufacturingOrderUnloaded);
            }

            if(null != tbBano)
            {
                Grid.SetColumn(tbBano, 15);
                Grid.SetRow(tbBano, 5);

                tbBano.Visibility = Visibility.Visible;
                tbBano.IsEnabled = true;

                tbBano.TabIndex = 3;

                lbBanoLabel.Visibility = Visibility.Visible;
                lbBanoLabel.IsEnabled = true;

                Grid.SetColumn(lbBanoLabel, 1);
                Grid.SetRow(lbBanoLabel, 5);
            }
            else
            {
                MessageBox.Show("No tbBano");
            }

            if(null != tbTransactionQuantity)
            {
                Grid.SetColumn(tbTransactionQuantity, 15);
                Grid.SetRow(tbTransactionQuantity, 6);
                tbTransactionQuantity.TabIndex = 4;
                Grid.SetColumn(lbTransactionQty, 1);
                Grid.SetRow(lbTransactionQty, 6);
            }
            else
            {
                MessageBox.Show("No tbTransactionQuantity");
            }

            if(null != tbToLocation)
            {
                Grid.SetColumn(tbToLocation, 15);
                Grid.SetRow(tbToLocation, 7);
                tbToLocation.TabIndex = 5;
                Grid.SetColumn(lbToLocation, 1);
                Grid.SetRow(lbToLocation, 7);
            }
            else
            {
                MessageBox.Show("No tbToLocation");
            }
            gController.add_RequestCompleted(OnRequestCompleted);
            gController.add_Requested(OnRequested);
            gController.add_Requesting(OnRequesting);
        }

        public function OntbOrderTypeGotFocus(sender: Object, e: RoutedEventArgs)
        {
            if(null != gContent)
            {
                var tbItemNumber = ScriptUtil.FindChild(gContent, "WRITNO");
                if(null != tbItemNumber)
                {
                    // if this control gets focus, move the focus to the item number textbox
                    tbItemNumber.Focus();
                }
            }
        }

        public function OngtbManufacturingOrderUnloaded(sender: Object, e: RoutedEventArgs)
        {
            // remove the events that we are subscribed to
            gtbManufacturingOrder.remove_Unloaded(OngtbManufacturingOrderUnloaded);
            gController.remove_RequestCompleted(OnRequestCompleted);
            gController.remove_Requested(OnRequested);
            gController.remove_Requesting(OnRequesting);
        }

        public function OnRequesting(sender: Object, e: CancelRequestEventArgs)
        {
            if(e.CommandType == MNEProtocol.CommandTypeKey)
            {
                // suppress the F4
                if(e.CommandValue == MNEProtocol.KeyF4)
                {
                    e.Cancel = true;
                }
            }
        }

        public function OnRequested(sender: Object, e: RequestEventArgs)
        {
            gdebug.WriteLine("OnRequested() Event Command Type: " + e.CommandType);
            gdebug.WriteLine("OnRequested() Event Command Value: " + e.CommandValue);
            if(e.CommandType == MNEProtocol.CommandTypeKey)
            {
                if(e.CommandValue == MNEProtocol.KeyEnter)
                {
                    var tbBano = ScriptUtil.FindChild(gContent, "WRBANO");

                    // save the Lot Number for later
                    // we do this because we have to do a 'double enter'
                    // to commit the transaction and then lot number value
                    // disappears
                    gLotNumber = tbBano.Text;
                }
            }
        }

        public function OnRequestCompleted(sender: Object, e: RequestEventArgs)
        {
            gdebug.WriteLine("OnRequestCompleted() Event Command Type: " + e.CommandType);
            gdebug.WriteLine("OnRequestCompleted() Event Command Value: " + e.CommandValue);
            gdebug.WriteLine("OnRequestCompleted() gLotNumber: " + gLotNumber);

            // verify that these are the events we are looking for!
            if(e.CommandType == MNEProtocol.CommandTypeKey)
            {
                if(e.CommandValue == MNEProtocol.KeyEnter)
                {
                    gdebug.WriteLine("OnRequestCompleted() gLotNumber: " + gLotNumber);
                    var tbBano = ScriptUtil.FindChild(gContent, "WRBANO");
                    var tbITNO = ScriptUtil.FindChild(gContent, "WRITNO");

                    // we will only set the lot number if we have an item in
                    // the Item TextBox, this way we don't
                    // accidently populate the lot number if we are getting
                    // the screen refresh after a committed transaction
                    if(false == String.IsNullOrEmpty(tbITNO.Text))
                    {
                        if(null != gController.Runtime.Result)
                        {
                            if(-1 != gController.Runtime.Result.IndexOf("<Msg>Confirm</Msg>"))
                            {
                                // repopulate the lot number
                                tbBano.Text = gLotNumber;
                                // press enter to commit the transaction
                                gController.PressKey(MNEProtocol.KeyEnter);
                            }
                        }
                    }
                    else
                    {
                        // reset the lot number back to null
                        gLotNumber = null;
                    }
                }
            }
        }
    }
}

 

Enjoy! :-)

About these ads
This entry was posted in Development, M3 / MoveX and tagged , , , , , , , , . Bookmark the permalink.

One Response to Modification Removal – MMS100 – Barcoding

  1. Paul Grooby says:

    Scott – I like the ability to move the controls around (lest of all the tab order/hiding fields). Nice. Can see a use for that to make some of the screens a lot quicker to use. Paul

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