Modification Removal – Freecap In OIS101 – BackgroundWorker and Adding Columns

In this post I will be rather wordy, as it marks a turning point – the realisation of a dream 😉 If you want to avoid some of the excessive chatter I’ve divided this in to sections so you can easily get past the BS – just scroll down to “The Problem”, or if you want to avoid explanations, scroll down to “And the Result” to see the script itself – extensively commented.

The Background

Sometimes when a business project is of sufficient size, snap decisions get made which have long term impacts – sadly with our implementation there were a number of wants in sensitive areas that required mods which were authorised for which we are still paying for over seven years later.

There are always holes in functionality – even with tailored software you’ll discover a perceived need in a certain piece of functionality that was overlooked or was only uncovered after extensive use of a product. People tend to think differently, and some work in rather unusual chaotic ways – so your efficient way may not be the efficient way for someone else.

For those of you that haven’t met me, I have been quite an advocate of modification removal through the use of jscripts – I’ve tried to raise awareness of some of the benefits of jscripts instead of mods in the NZ M3 community – even made a few jabs at Lawson to try to get them to get the word out to the consultants. Infact one of the selling points of upgrading to M3 10.1 here was that we would have access to two very kewl technologies – Smart Office (yay!) and WebServices which should allow us to remove almost all of our modifications.

To keep short timeframes to keep our staff focused, our upgrade was inplace – all mods were uplifted as is with some consulting time spared to provide areas that we can look at process improvement/mod removal. We would then quietly start working on modification removal through process change or jscripts throughout the 2011 year. This didn’t pan out due to spending the better part of 2011 scrambling to get our network running properly after some substantial earthquakes.

Why this long post? Well, if you’ve been reading this blog for a while, you’ll notice I like the sound of my own words 😉 And well, to be honest, I am quite excited to finally be able to move forward and clean up a lot of loose ends.

The Problem

We are predominantly an export company, ~80 – 85% of our product gets exported in shipping containers. A single container could have a single SKU of product, or it could have a dozen different SKUs. However, one thing that we have to do is ensure containers are packed to capacity. A container that isn’t packed to capacity increases the chances of product getting damaged considerably and isn’t very efficient.

We had previous systems where we could key in an order and it would calculate the approximate percentage of a 20′ container they would consume – these systems varied from a spreadsheet to a sprawling database in FileMaker. Our staff could determine how many containers needed to be booked or if an order needed to be increased or decreased.

In the efforts to remove peripheral repositories of data, a modification was made to OIS101 so it would take the Free Cap Units in MMS001 and calculate a container percentage of each individual line. It would then also provide a total percentage. The Free Cap Units in MMS001 were expressed as a percentage of a 20′ container that a single unit would consume.

Given the modification is in a rather sensitive area – which has been proven to create the occasional head-ache, and given the modification is purely for display, it would be nice to have it removed. Also, knew it would be challenging as there would be a need for several ‘new’ techniques to achieve success.

Sometimes the Simplest Things…

…can be insanely complicated. 🙂

The concept was that I’d add a new column to the ListView in the Init(), I’d spin up a BackgroundWorker thread to retrieve the Free Cap units through webservices – this WebService would actually use the SQL component and request all of the lines and do the actual math to calculate the freecap – returning each line with the appropriately calculated free cap value. I’d iterate through the lines, extract the ITNO and the ORQT and search for them in the data returned from the webservice so I could extract the container percentage. Then I would populate my column with the data and finally create a total which I’d display in a TextBox I added by simply adding all of the container percentages together and writing it out to a TextBox.

Adding columns in Smart Office wasn’t hard, as I previously posted. Actually getting them to work properly however was non-trivial – so the entire project got shelved due to frustration and time commitments to disaster recovery.

But then I spied a posting by Thibaud http://thibaudatwork.wordpress.com/2011/09/22/how-to-add-a-column-to-a-list/ which described how to do it quite nicely. I did some work based on the posting and got it working quite happily, so I was nearly ready to spend some time on this again.

Over and above this, as I mentioned above I had wanted to look in to spinning it up a background thread – I had been toying with the concept but as part of it wanted to figure out how to use delegates in jscript to get around updating content on controls from a different thread (only the UI thread can update controls). Eventually I re-read the .Net documentation and realised that the OnWorkerCompleted event was called on the UI thread – I swear the MSDN documentation didn’t say that when I first started playing with BackgroundWorker but then I am prone to skim reading :-). Shortly after that and much to my delight Karin posted an article on the BackgroundWorker thread http://lawsonsmartoffice.wordpress.com/2011/12/19/calling-m3-apis-in-jscript-on-a-background-thread/ – I did have to laugh at the Simpsons picture.

So, all of the pieces were falling in to place…

The WebService

I won’t go through the creation of the WebService as I have done so in a previous post – and this particular posting is long enough as is. However, here is a snapshot of the completed WebService

 

 

 

WebService Tweaks from Previous Code

The previous code that I had used to process webservice results wasn’t up to the task of what I wanted to achieve. I needed to take the stream that was returned and then read it in a controlled fashion, creating objects from a new class which would be added to an array that we could search.

To this end, I made significant changes to the decodeSOAPResult() function, including needing to orientate my code – getting it to the correct depth to start processing the OrderLineCapItem elements. To do this I created this wee loop.

                // loop through the incoming stream until
                // we get to our OrderLineCapItem Element
                while(xmrReader.Read())
                {
                    if(xmrReader.NodeType == XmlNodeType.Element)
                    {
                        if(false == String.IsNullOrEmpty(xmrReader.Name))
                        {
                            if(0 == String.Compare("OrderLineCapItem",xmrReader.Name))
                            {
                                break;
                            }
                        }
                    }
                }

Once we were at our starting point we could extract useful information. I would process an element at a time, extracting the value and populating a OrderLineCapItem object with the values until we had populated all of our values. Then it would be pushed in to an array.

 

Not All ListViews are Created Equal

I knew that not all of our views had the Item Number and the Quantity in the same column, so this meant I needed to be pretty dynamic in the way that I determined their location. After a little bit of fumbling around I noticed that the ListControl.Columns had the four digit name of the column, so I created a little loop that would look for ITNO and ORQT. As it turns out, one of the Views used ORQA instead of ORQT – so we also look for that.

            // loop through the ListControl columns looking for
            // specific column names (ITNO, ORQT, ORQA)
            for(var j = 0; j < lcListControl.Columns.Count; j++)
            {
                // call me paranoid
                if(false == String.IsNullOrEmpty(lcListControl.Columns[j]))
                {
                    // we're searching for the position of ITNO
                    if(0 == String.Compare("ITNO", lcListControl.Columns[j]))
                    {
                        giITNOPos = j;
                        gdebug.WriteLine("ITNO found! " + j);
                    }
                    // now search for the quantity
                    else if(0 == String.Compare("ORQT", lcListControl.Columns[j]))
                    {
                        giORQTPos = j;
                        gdebug.WriteLine("ORQT found! " + j);
                    }
                    // and again, the quantity
                    else if(0 == String.Compare("ORQA", lcListControl.Columns[j]))
                    {
                        giORQTPos = j;
                        gdebug.WriteLine("ORQA found! " + j);
                    }
                }
                // if we have found the both the item number
                // and the quantity, then we can exit the loop
                if( (-1 != ITNOPos) && (-1 != ORQTPos) )
                {
                    break;
                }
            }

I also had my memory refreshed :-[ that there are events will remove controls from the panel and call the Init(), this meant that I needed to dynamically determine where the percentage column that I added was. Should be trivial, but it wasn’t quite as easy as I hoped. I ended up creating a function which would loop through the ListView columns and search for the text that I added.

        // look through the column contents for the string
        // % of Container
        // this will be the column that we should be working
        // with
        public function findPercentageColumn(acColumns) : Int32
        {
            var result : Int32 = -1;
 
            // reverse loop through the columns (our column will tend to be at the end)
            // for(var i = 0; i < acColumns.Count; i++)
            for(var i = (acColumns.Count-1); -1 < i; i--)
            {
                // retrieve a column header
                var chColumnHeader = acColumns[i].Header;
 
                // extract its content (most of the standard ones
                // will have StackPanels, but we're lazy and
                // haven't bothered, we just have some text
                var strContent = chColumnHeader.Content;
 
                if(0 == String.Compare("% of Container", strContent))
                {
                    // we have found the column we are after
                    // now we should return the result and break
                    // from the loop
                    result = i;
                    break;
                }
            }
 
            return(result);
        }

And the Result…

And finally we get to the code itself. I still have user validation to occur (mainly around pulling out the modification itself rather than the script), but we’re looking pretty good. Don’t forget to change the username and password for logging in to the webservices in createSOAPRequest()

As always, code is presented as is without any warranty of any sort. It may or may not work for you, create world peace or put an end to reality tv…

import System;
import System.Windows;
import System.Windows.Controls;
import MForms;
 
import System.IO;
import System.Net;
// import System.Collections.Generic;
//import System.Collections.ObjectModel;
import System.ComponentModel;
 
import Mango.UI.Core;
 
import System.Xml;
import System.Xml.Serialization;
import System.Web.Services.Protocols;
 
import Mango.UI.Services.Lists;
 
package MForms.JScript
{
    class ColumnTest_V007
    {
        var gdebug;
 
        var giITNOPos : Int32 = -1;    // item number position
        var giORQTPos : Int32 = -1;    // order quantity position
 
        var gtbTotalPercentage : TextBox = null;
 
        var gController = null;
        var glvListView = null;
 
        var gobjArray : Array = null;
 
        var giItemsPopulatedUpTo : Int32 = 0;   // this keeps track of the number of rows that we populated in our column
        var giColumnIndex : Int32 = -1;          // this is where we have created our column
 
        var gbwBackgroundWorker : BackgroundWorker = new BackgroundWorker();
 
        var gstrOrderNumber = "";
 
        // this is how we will format percentage strings - we want consistency in the
        // two areas that we use this
        // N4 = number, to 4 decimal places
        var gstrPercentageFormat = "N4";
 
        public function Init(element: Object, args: Object, controller : Object, debug : Object)
        {
            gdebug = debug;
            gController = controller;
 
            var content : Object = controller.RenderEngine.Content;
 
            // we set the items populated up to back to 0 here so when we add/delete/refresh the listview
            // we actually repopulate everything
            giItemsPopulatedUpTo = 0;
 
            // create a label with the content of "Container %" and add it our panel
            var lblTotalPercentageTotal = new Label();
            lblTotalPercentageTotal.Content = "Container %:";
 
            // for consistency we *should* use the Lawson settings, for
            // height, margin and padding etc so here goes 🙂
            lblTotalPercentageTotal.MinHeight = Configuration.ControlHeight;
            lblTotalPercentageTotal.Height = Configuration.ControlHeight;
            lblTotalPercentageTotal.Margin = Configuration.ControlMargin;;
            lblTotalPercentageTotal.Padding = Configuration.ControlPadding;;
            // set the label position
            Grid.SetColumnSpan(lblTotalPercentageTotal, 13);
            Grid.SetColumn(lblTotalPercentageTotal, 55);
            Grid.SetRow(lblTotalPercentageTotal, 1);
            // finally add the label to the panel
            content.Children.Add(lblTotalPercentageTotal);
 
            // create the actual TextBox which will show the container percentage
            gtbTotalPercentage = new TextBox();
            // set it to read only
            gtbTotalPercentage.IsReadOnly = true;
            gtbTotalPercentage.IsEnabled = false;
            gtbTotalPercentage.Width = 100;
 
            gtbTotalPercentage.MinHeight = Configuration.ControlHeight;
            gtbTotalPercentage.Height = Configuration.ControlHeight;
            gtbTotalPercentage.Margin = Configuration.ControlMargin;;
            gtbTotalPercentage.Padding = Configuration.ControlPadding;;
 
 
            // set the TextBox position
            Grid.SetColumnSpan(gtbTotalPercentage, 13);
            Grid.SetColumn(gtbTotalPercentage, 65);
            Grid.SetRow(gtbTotalPercentage, 1);
                
            // finally add the TextBox to the grid
            content.Children.Add(gtbTotalPercentage);
 
            // we want to know when the TotalPercentage TextBox is unloaded,
            // this gives us an opportunity to do some clean up
            gtbTotalPercentage.add_Unloaded(OngtbTotalPercentageUnloaded);
 
            // we want to keep track on RequestCompleted and Requested events
            // RequestCompleted is particularly importanted as we need to
            // handle page down events where additional data may be added
            // to the listview - which we then need to populate
            gController.add_RequestCompleted(OnRequestCompleted);
            // gController.add_Requested(OnRequested);
 
            // we want to subscribe to the DoWork and RunWorkerCompleted
            // events of our background worker thread.
            // our Background worker thread will do the webservice call
            // and we will then use the RunWorkerCompleted to actually
            // populate our results
            gbwBackgroundWorker.add_DoWork(OnDoWork);
            gbwBackgroundWorker.add_RunWorkerCompleted(OnRunWorkerCompleted);
 
            glvListView = controller.RenderEngine.ListControl.ListView;
 
            // we need to retrieve our order number
            var tbOrderNUmber = ScriptUtil.FindChild(content, "OAORNO");
 
            // store the order number for usage elsewhere
            gstrOrderNumber = tbOrderNUmber.Text;
 
            // add our new column
            createColumnHeader(controller.RenderEngine.ListControl, "% of Container");
 
            // run the backgroundworker threads task
            gbwBackgroundWorker.RunWorkerAsync();
 
            //createColumnHeader(controller.RenderEngine.ListControl, "% of Container");
        }
 
        // this function actually goes out, constructs and calls our webservice
        // on our background thread
        public function OnDoWork(sender : Object, e : DoWorkEventArgs)
        {
            try
            {
                // gobjArray = doRequest(createSOAPRequest("0000071780"));
                // gobjArray = doRequest(createSOAPRequest("0000037455"));
                gobjArray = doRequest(createSOAPRequest(gstrOrderNumber));
            }
            catch(ex)
            {
                gdebug.WriteLine("OnDoWork() - Exception: " + ex.message);
            }
        }
 
        // once our backgroundworker thread has finished, we need to update the UI
        public function OnRunWorkerCompleted(sender : Object, e : RunWorkerCompletedEventArgs)
        {
            try
            {
                // populate the new column with the new container percentage values
                populateNewColumn(glvListView, gobjArray);
                // update the total textbox with the total percentage
                populateContainerTotalTextBox(gobjArray);
            }
            catch(ex)
            {
                gdebug.WriteLine("OnRunWorkerCompleted() - Exception: " + ex.message);
            }
        }
 
        
        public function OngtbTotalPercentageUnloaded(sender: Object, e: RoutedEventArgs)
        {
            cleanup();
        }
 
        // this function will go out and do some cleanup
        public function cleanup()
        {
            // remove the events that we have subscribed to
            gbwBackgroundWorker.remove_DoWork(OnDoWork);
            gbwBackgroundWorker.remove_RunWorkerCompleted(OnRunWorkerCompleted);
            
            gController.remove_RequestCompleted(OnRequestCompleted);
            gtbTotalPercentage.remove_Unloaded(OngtbTotalPercentageUnloaded);
        }
 
        // we will calculate the total percentage our entire order consumes
        public function populateContainerTotalTextBox(aobjArray : Array)
        {
            var dblTotal : double = 0.0;
            try
            {
                // loop through the array returned from our webservice
                // the array will be ALL of the lines in the order,
                // not just the lines that Smart Office currently has loaded
                for(var i = 0; i < aobjArray.length; i++)
                {
                    var olciCurrent : OrderLineCapItem = aobjArray[i];
                    if(null != olciCurrent)
                    {
                        // In the past I have seen odd values in the lower
                        // part of doubles
                        if(olciCurrent.CONTAINERPERCENTAGE > 0.000001)
                        {
                            dblTotal += (olciCurrent.CONTAINERPERCENTAGE/100);
                        }
                    }
                }
                // format and display based on the value in the gstrPercentageFormat
                gtbTotalPercentage.Text = dblTotal.ToString(gstrPercentageFormat);
            }
            catch(ex)
            {
                gdebug.WriteLine("populateContainerTotalTextBox() Exception: " + ex.Message);
            }
        }
 
        // go our and do the heavy lifting and populate the new column
        // this function doesn't need to do any calculations as we
        // will actually just search through our array of values looking
        // for a match on quantity and item code
        public function populateNewColumn(alvListView, aobjArray : Array)
        {
            try
            {
                if(null != aobjArray)
                {
                    // we need the positions of the Item Number and Quantity columns
                    // so we have seed information to search on
                    if( (-1 != giITNOPos) && (-1 != giORQTPos) )
                    {
                        var itItemList = alvListView.Items;
 
                        for(var i = giItemsPopulatedUpTo; i < itItemList.Count; i++)
                        {
                            var clColumns = alvListView.View.Columns;
                            var itItem = itItemList[i];
                            var itOldArray = itItem.Items;
 
                            // create an array of strings that will store our new 
                            // values - we will be removing the entire row and 
                            // adding a replacement
                            var itNewArray = new String[clColumns.Count];
 
                            var strBase : Object[] = itItemList[i].Items[giITNOPos];
 
                            // retrieve the Item Number
                            var strITNO : String = strBase[giITNOPos];
 
                            // retrieve the quantity
                            var strORQT : String = strBase[giORQTPos];
 
                            // look for an entry that matches both the item number and quantity
                            var olciMatched = searchForProduct(aobjArray, strITNO , strORQT);
                            
                            if(null != olciMatched)
                            {
                                // copy the old values to the array that will be displayed
                                itOldArray.CopyTo(itNewArray, 0);
 
                                // set our added columns value to the container percentage
                                // we also make sure that we format the value as per
                                // the settings in gstrPercentageFormat
                                itNewArray[giColumnIndex] = (olciMatched.CONTAINERPERCENTAGE/100).ToString(gstrPercentageFormat);
 
                                // change the items so they are our new values
                                itItem.Items = itNewArray;
 
                                // remove the original row from our listview
                                itItemList.RemoveAt(i);
                                // insert our new row - maintaining its position in the listview
                                itItemList.Insert(i, itItem);
                            }
                        }
                        // keep track of where we have populated up to
                        giItemsPopulatedUpTo = itItemList.Count;        // we don't want to populate the column if it is already been populated
                    }
                }
            }
            catch(exouter)
            {
                gdebug.WriteLine("populateNewColumn() Exception: " + exouter.message);
            }
        }
 
        // look through the column contents for the string
        // % of Container
        // this will be the column that we should be working
        // with
        public function findPercentageColumn(acColumns) : Int32
        {
            var result : Int32 = -1;
 
            // reverse loop through the columns (our column will tend to be at the end)
            // for(var i = 0; i < acColumns.Count; i++)
            for(var i = (acColumns.Count-1); -1 < i; i--)
            {
                // retrieve a column header
                var chColumnHeader = acColumns[i].Header;
 
                // extract its content (most of the standard ones
                // will have StackPanels, but we're lazy and
                // haven't bothered, we just have some text
                var strContent = chColumnHeader.Content;
 
                if(0 == String.Compare("% of Container", strContent))
                {
                    // we have found the column we are after
                    // now we should return the result and break
                    // from the loop
                    result = i;
                    break;
                }
            }
 
            return(result);
        }
 
        // this function will go out and create our column header
        public function createColumnHeader(alcControl, astrColumnName : String)
        {
            var lcListControl = alcControl;
            var lvListView = alcControl.ListView;
            var clColumns = lvListView.View.Columns;
 
            // locate the percentage column, if it doesn't exist
            // then we should add it.
            // we do this here as when certain operations are called
            // our controls get removed and the Init() is called
            // again.
            giColumnIndex = findPercentageColumn(clColumns);
            if(-1 == giColumnIndex)
            {
                var gvchColumnHeader = new GridViewColumnHeader();
                var gvcColumn = new GridViewColumn();
 
                // set the column name, we should really use a StackPanel
                // here so everything lines but, but I'm lazy 😉
                gvchColumnHeader.Content = astrColumnName;
                // set the header of the column to our new header object
                gvcColumn.Header = gvchColumnHeader;
 
                gvcColumn.CellTemplateSelector = new ListCellTemplateSelector(clColumns.Count, alcControl.Columns);
 
                // finally, attach the column to our columns list
                clColumns.Add(gvcColumn);
 
                // store the position of our new column
                giColumnIndex = clColumns.Count - 1;
            }
 
 
            // loop through the ListControl columns looking for
            // specific column names (ITNO, ORQT, ORQA)
            for(var j = 0; j < lcListControl.Columns.Count; j++)
            {
                // call me paranoid
                if(false == String.IsNullOrEmpty(lcListControl.Columns[j]))
                {
                    // we're searching for the position of ITNO
                    if(0 == String.Compare("ITNO", lcListControl.Columns[j]))
                    {
                        giITNOPos = j;
                        gdebug.WriteLine("ITNO found! " + j);
                    }
                    // now search for the quantity
                    else if(0 == String.Compare("ORQT", lcListControl.Columns[j]))
                    {
                        giORQTPos = j;
                        gdebug.WriteLine("ORQT found! " + j);
                    }
                    // and again, the quantity
                    else if(0 == String.Compare("ORQA", lcListControl.Columns[j]))
                    {
                        giORQTPos = j;
                        gdebug.WriteLine("ORQA found! " + j);
                    }
                }
                // if we have found the both the item number
                // and the quantity, then we can exit the loop
                if( (-1 != ITNOPos) && (-1 != ORQTPos) )
                {
                    break;
                }
            }
        }
 
        // this function will search through the array returned from our webservice
        // for a matching item number and quantity, to finally return the associated
        // OrderLineCapItem object
        private function searchForProduct(arArray, astrItemNumber : String, astrQuantity : String) : OrderLineCapItem
        {
            var result : OrderLineCapItem = null;
 
            //gdebug.WriteLine("searchForProduct Array Length = " + arArray.length);
            for(var i = 0; i < arArray.length; i++)
            {
                var olciCurrent : OrderLineCapItem = arArray[i];
                if(null != olciCurrent)
                {
                    if((olciCurrent.OBITNO != null) && (olciCurrent.OBORQT != null))
                    {
                        // extract the item number, trim any trailing whitespaces
                        var strItem : String = olciCurrent.OBITNO.Trim();
                        var strQuantity : String = olciCurrent.OBORQT;
 
                        if(0 == String.Compare(strItem, astrItemNumber))
                        {
                            if(0 == String.Compare(strQuantity, astrQuantity))
                            {
                                // we have a match, return the OrderLineCapItem
                                // and break from the loop
                                result = olciCurrent;
                                break;
                            }
                        }
                    }
                }
            }
 
            return(result);
        }
 
        public function createSOAPRequest(astrOrderNumber : String) : String
        {
            return("<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:mws2=\"http://mws.intentia.net/mws2\" xmlns:get=\"http://www.indfish.co.nz/FreeCapsSQL/GetFreeCaps\"><soapenv:Header><mws2:mws><mws2:user>potatoit</mws2:user><mws2:password>potatoit </mws2:password><mws2:company>100</mws2:company><mws2:division>IFL</mws2:division></mws2:mws></soapenv:Header><soapenv:Body><get:GetFreeCaps><get:OrderNumber>" + astrOrderNumber + "</get:OrderNumber></get:GetFreeCaps></soapenv:Body></soapenv:Envelope>");
        }
 
        // in previous situations when using WebServices I didn't need to
        // worry so much about a list of data being returned, this meant
        // that tweaks are needed to make this work with a list of data
        public function doRequest(astrXMLRequest : String) : Array
        {
            var result : Array;
            // we are going to use the HttpWebRequest object
            // http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.aspx
            // and we want to connect to the ItemFreeCaps2 service
            var hwrRequest : HttpWebRequest = WebRequest.Create("http://wlmx02.indfish.co.nz:12000/lwstest/services/FreeCapsSQL");
 
            // ensure we actually managed to create something
            if(null != hwrRequest)
            {
                // here we're defining our actions and content types
                hwrRequest.Headers.Add("SOAPAction","\"\"");
                hwrRequest.ContentType = "text/xml;charset=\"utf-8\"";
                hwrRequest.Method = "POST";
                hwrRequest.Accept = "text/xml";
 
                hwrRequest.Proxy = GlobalProxySelection.GetEmptyWebProxy();
 
                // we are going to use a stream to write out our request (and also read it later)
                var strmStream : Stream = hwrRequest.GetRequestStream();
                if(null != strmStream)
                {
                    // SOAP is basically just xml, so we are going to use the XML framework
                    // to make our lives easier.
                    // Create an XML Document
                    var xmdDocument : XmlDocument = new XmlDocument();
                    if(null != xmdDocument)
                    {
                        // we then add the String to our XML document
                        xmdDocument.LoadXml(astrXMLRequest);
 
                        // the save of the document to our stream actually sends the request
                        // to the server
                        xmdDocument.Save(strmStream);
 
                        // close our stream
                        strmStream.Close();
 
                        // this section is wrapped in a try .. catch()
                        // block because I had a lot of problems getting
                        // this running initially.
                        try
                        {
                            // now we want to get a response
                            var wresponse : WebResponse = hwrRequest.GetResponse();
                            if(null != wresponse)
                            {
                                // we like using streams, so get the stream
                                // connection
                                strmStream = wresponse.GetResponseStream();
                                if(null != strmStream)
                                {
                                    // create a streamreader to retrieve the data
                                    var srStreamReader : StreamReader = new StreamReader(strmStream);
                                    if(null != srStreamReader)
                                    {
                                        // and finally we pass the stream handle to the decode
                                        // function
                                        var objResult = decodeSOAPResult(srStreamReader);
                                        if(null != objResult)
                                        {
                                            result = objResult;
                                        }
                                        else
                                        {
                                            gdebug.WriteLine("no result from request");
                                        }
 
                                        // close the response
                                        wresponse.Close();
 
                                        // close the stream reader
                                        srStreamReader.Close();
                                    }
                                }
                            }
                            else
                            {
                                gdebug.WriteLine("No Response was returned");
                            }
                        }
                        catch(e)
                        {
                            gdebug.WriteLine("Exception: " + e.message);
                        }
                    }
                }
            }
            else
            {
                gdebug.WriteLine("doRequest() unable to create");
            }
            return(result);
        }
 
        public function decodeSOAPResult(asrStream : StreamReader)
        {
            var result;
            try
            {
                // create an XmlReader from the stream handle
                var xmrReader = XmlReader.Create(asrStream);
 
                // loop through the incoming stream until
                // we get to our OrderLineCapItem Element
                while(xmrReader.Read())
                {
                    if(xmrReader.NodeType == XmlNodeType.Element)
                    {
                        if(false == String.IsNullOrEmpty(xmrReader.Name))
                        {
                            if(0 == String.Compare("OrderLineCapItem",xmrReader.Name))
                            {
                                break;
                            }
                        }
                    }
                }
                
                // this is the array where we will be storing our objects
                var olciArray = new Array();
 
                // continue reading until we hit the end of file (EOF)
                while(false == xmrReader.EOF)
                {
                    var olciCurrent : OrderLineCapItem = new OrderLineCapItem();
                    xmrReader.ReadStartElement("OrderLineCapItem");
 
                    xmrReader.ReadStartElement("OBORNO");
                    
                    olciCurrent.OBORNO = xmrReader.ReadString();
                    xmrReader.ReadEndElement();
                    xmrReader.ReadStartElement("OBITNO");
                    
                    olciCurrent.OBITNO = xmrReader.ReadString();
                    xmrReader.ReadEndElement();
                    xmrReader.ReadStartElement("MMFCU1");
                    
                    olciCurrent.MMFCU1 = xmrReader.ReadString();
                    xmrReader.ReadEndElement();
                    xmrReader.ReadStartElement("OBORQT");
                    
                    olciCurrent.OBORQT = xmrReader.ReadString();
                    xmrReader.ReadEndElement();
                    xmrReader.ReadStartElement("CONTAINERPERCENTAGE");
                    
                    olciCurrent.CONTAINERPERCENTAGE = xmrReader.ReadString();
                    xmrReader.ReadEndElement();
 
                    xmrReader.ReadEndElement();
 
                    // add our newly populate object to the array
                    olciArray.push(olciCurrent);
 
                    if(false == String.IsNullOrEmpty(xmrReader.Name))
                    {
                        if(0 != String.Compare("OrderLineCapItem",xmrReader.Name))
                        {
                            // exit if the next element isn't an OrderLineCapItem element
                            break;
                        }
                    }
 
                }
                // close the reader
                xmrReader.Close();
                // return our array
                result = olciArray;
            }
            catch(ex)
            {
                gdebug.WriteLine("Exception decoding the SOAP request " + ex.message);
            }
            return(result);
        }
 
        public function OnRequestCompleted(sender: Object, e: RequestEventArgs)
        {
            // verify that these are the events we are looking for!
            if(e.CommandType == MNEProtocol.CommandTypePage)
            {
                // we had a page event, this means that we may have new data
                // that needs its percentages to be updated
                populateNewColumn(glvListView, gobjArray);
            }
        }
    }
 
    public class OrderLineCapItem
    {
        var OBORNO : String;
        var OBITNO : String;
        var MMFCU1 : double;
        var OBORQT : double;
        var CONTAINERPERCENTAGE : double;
    }
}
 

Happy coding!

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

4 Responses to Modification Removal – Freecap In OIS101 – BackgroundWorker and Adding Columns

  1. Maxric says:

    Hi Scott,

    Have you ever tried to add a column in a list that contains editable cells ? (like MMS424/B1 for example). The command “CopyTo” fails because the editable cells do not have the type System.String. You cannot remove and insert the new rows so easely.

    On smartofficeblog.com, Norp said that editable cells values could be retrieved by MForms.MFormsUtil.GetCellText(lviItem,k) but i wonder how to create the new rows… ? When you manually pack in MMS424 for example, the packed quantity is filled by default but with the script i have written it is blank. Any idea ?

    • potatoit says:

      Hi Maxric,

      I haven’t personally done it, but I would probably recommend asking over on the Smart Office blog.

      If you dig in to the ItemsSource it requires a fair amount to unwind how it’s built. For example, the first item in the ItemsSource is an observable collection of Mango.UI.Services.Lists.ListRow which represents each row. The ListRow has an Items array which stores each cell in the row. If you do a GetType() on each of those cells in a ListView that has an editable column, then you’ll see the read only columns return “System.String”, the Editable cells are Mango.UI.Services.Lists.EditableCell

      So you could reverse engineer it…but…

    • potatoit says:

      Ok, I did some digging and experimentation. If you look at the example from the Smart Office Blog here:
      http://smartofficeblog.com/2012/02/13/adding-a-new-column-in-a-m3-list/

      line 43 is
      var newItems = new String[columnCount];

      change it so it is var newItems = new Object[columnCount];

      and it should work.

      Cheers,
      Scott

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