Stephen Smith's Blog

Musings on Machine Learning…

Archive for December 2013

Looking Back on 2013 and Forward to 2014

with 6 comments

Introduction

2013 was a very busy year for all of us on the Sage 300 ERP R&D team. Most of us are located in Richmond, BC, but we have remote team members in Kelowna, BC, Victoria, BC, North Carolina, Chennai, India and Bangalore, India. As we approach New Year 2014 let’s look back on what we’ve been up to this past year.

Happy New Year 2014 replace 2013 concept on the sea beach

Product Update 1

We started the year by releasing the Sage 300 ERP 2012 Product Update 1. This included new features like Colorful Companies and a major new release of Canadian and US Payroll.

As always a lot of work goes into product sustainment. Fixing problems caused by new versions of Windows, strange interactions discovered with other programs and other bugs. Diagnosing and tracking down the root causes of these can be quite demanding work, but is completely necessary to keep our customers happy. Quality is always a high priority and always an ongoing concern.

Sage 300 ERP 2014

We have a core team that works on improving the Sage 300 ERP core product and they spent their time working on the Sage 300 ERP 2014 release. This version has been released to a limited VIP set of customers and will start to be publicized to the full Sage 300 audience in March.

I wrote quite a few articles on the various things in this release including:

Another major improvement in Sage 300 EPR 2014 that I haven’t blogged about yet, is the Crystal Reports Support. With this release we are bundling Service Pack 8 of the Crystal Reports 2011 runtime. This service pack has some major performance improvements over the version of the Crystal Reports Runtime bundled with the 2012 version. Actually someone in our office reverse engineered the Crystal runtime to figure out why it was becoming quite slow in some cases and was able to feed this information to the Crystal Reports development team (that is located nearby in downtown Vancouver) so that we could get this rectified. If you are having trouble with performance on the 2012 version, you can now get the Service Pack 8 from Crystal’s website and install this. With this service pack we now get performance that is consistently better than that experience with the older runtime bundled with 6.0A, but now with all the extra features and supported platforms that are provided as part of the their 2011 version. Generally being in the same city makes working with the Crystal development team easier, plus we have a lot of cross-over since Crystal Reports was originally developed as an add-in reporting tool for Accpac Plus.

With this release we add support for Windows 8.1, Windows Server 2012 R2 and Office 2013. We also have added support to run when Windows DEP is turned on. Generally updating anything to ensure we run well in any modern environment.

Windows 8 and Windows Server 2012 had a bug in their printer drivers that caused many fonts to be converted to bitmaps. This then defeated products like PrintBoss as well as things like Micr checks and anything else that is very sensitive to fonts. This problem was fixed by Microsoft in Windows 8.1 and Server 2012 R2. So if you are having trouble with this, make sure you upgrade these (which is free).

Sage 300 Online

Another major project was the new Sage 300 Online, which we unveiled at Sage Summit. This is a major revamp of our hosted offering. Now we host Sage 300 in the Microsoft Azure cloud and have greatly improved the manner in which we virtualize Sage 300. Authentication is handled by SageID which is also used for our connected services and Sage One. All the management, upgrades and tuning is handle by Sage so that customers don’t need to worry about this.

online

We are managing the site with a new DevOps team that consists of members dedicated to the site along with other members of R&D and IS. This is a new model for running the site, managing loads and keeping everything and everyone up to date.

Sage 300 Online has now started to on-board our first customers. At first we are starting out slowly to ensure everything keeps working fine, but expect to see a major push for this services in a few months.

We are now switching to a continuous delivery model, so expect to see improvement to this service being put on-line frequently.

Sage Connected Services

We have team members helping out with the Sage Connected Services. This year we released: Sage Mobile Service, Sage Mobile Sales and Sage Billing and Payments. We are now working on new versions of these as well as working on building out the Sage Data Cloud platform and bringing out further mobile and web applications.

The cloud part of these applications is developed for the Microsoft Azure PaaS platform. The mobile applications are written as native applications for iOS and in the future Android.

Our core team is responsible for maintaining the Sage 300 connector which transfers data back and forth from an on premise database and the Sage Data Cloud.

Next Year

As we go into 2014 we are building on all these initiatives. The general roadmap is still what I outlined here however as we go into the year, many of the details will be announced. Such as what is in the next version of Sage 300 ERP, what will be in all the Product Updates and what will the next version of Sage 300 Online actually look like.

Besides product changes, we will be having a lot of process changes. Now that Sage 300 Online is launched, we will be living in a continuous delivery, DevOps and cloud first world. Seamless invisible upgrades will be the norm, features can be deployed to all our cloud customers instantly and frequently. Ensuring that all these new processes run smoothly will be one of our major challenges in 2014.

Summary

Sage 300 ERP is an important strategic product for Sage. There is a lot of exciting development going on. We accomplished a lot in 2013 and look forward to even more in 2014. I am really looking forward to attending our various conferences this year to show off all the exciting work that is currently going on behind the scenes.

Written by smist08

December 28, 2013 at 6:33 pm

Error Reporting in Sage 300 ERP

with 18 comments

Introduction

A very important aspect of all applications is how they handle errors and how they inform the end user about these errors. When everything is entered properly and people take what is called the “happy path” through the program then there is no issue. But end users will stray from the “happy path” and other circumstances can conspire to cause the need for informing the user of unusual circumstances.

In our previous blog postings on using the .Net API we have been deferring our discussion of error reporting, but now we are going to tackle it and add error reporting to the ASP.Net MVC sample program we started last week. In doing so we will introduce some new concepts including starting to use JQuery and Microsoft’s Unobtrusive Ajax.

Errors

Generally when we refer to errors in this articles we will also mean warnings and informational messages. Often you hit save on a form and if something is wrong then you get a message box telling you what was wrong (and maybe even what to do about it). However this is bit of an oversimplification. Sage 300 ERP is a three tier client server application. Many of the errors originate in the business logic or the database server. These may be running as Windows services and have no way of doing any user interaction. They can’t simply popup a message box. The error message must be transmitted to where ever the UI is running (say in a web browser somewhere on the Internet) and displayed there in a nice form that fits in with the general design of the form.

Further there may be more than one error. It is certainly annoying to have error messages popup one at a time to be answered and it is also very annoying to Save have one error message appear and correct the thing wrong, hit save again and then have a further thing wrong and so on.

To solve these problems we collect errors, warnings and messages up into a collection which is maintained during processing and forwarded up to the higher levels when processing is complete. We see this in the .Net API with the Errors collection on the Session object. As processing proceeds any business logic component can add messages to this collection. This collection can then be processed by the UI and cleared after it has reported the errors.

All the actual error messages referenced from the business logic are stored in Windows resource files and one of these is provided for each language. So the API used by the Business Logic will access the correct language resource file for the language of the Sage 300 user associated with the session object.

Inside the collection, each error has a priority such as Severe Error, Error, Security, Warning or Message. This way we can decide further how to display the error. Perhaps have the error dialog title bar red for errors, blue for warnings and green for messages.

Exceptions

Inside our .Net API we will return a return code for simple things that should be handled by the program. For instance if we go to read a record, we return false if it doesn’t exist, true if it does. But for more abnormal things we will throw an exception. You should always catch these exceptions, since even if you are debugging a program, these messages can be very helpful.

Just because an exception is thrown, doesn’t mean there is an error on the error stack. The exception might be because of some other exception, say from the .Net runtime. Because of this you will see in our exception handler, that if there is no error from Sage 300 then we display the error that is part of the exception (perhaps from .Net).

Sample Program

To add error reporting to our ASP.Net MVC sample programming we are going to start introducing how to program richer functionality in the browser. As we left off you just hit submit, the processing was done and the page was refreshed completely. This is rather old school web programming and a bit better user interaction is expected these days. Now I’ve updated the sample ARInvEntry sample (located here) to do an Ajax request rather than a page submit request. As a result the page isn’t completely redrawn and we can do a bit more processing. In this case we will use JQuery to put up a dialog box with any messages, warnings or errors that were encountered. Normally when you save an Invoice there is no success message displayed, but here we will put up a success message as well.

First off now that we are programming in JavaScript in the Browser, this is an interpreted environment which means there is no compiler to catch dumb programming mistakes and typos. One thing is to watch the syntax highlighting in the editor and use intelli-sense to avoid some typos. The other things is that we are passing data structures from C# on the server to JavaScript on the client. Which means there is no validation that C# and JavaScript have the same understanding of the object. Further JavaScript tends to silently ignore errors and keep on going, so you can be rather mystified when things don’t work. Some good tools to look for errors are Firebug and Fiddler. Trying different browsers can help also.

First I changed the page submit call to an Ajax call using Microsoft’s unobtrusive JavaScript library.

        @using (Ajax.BeginForm("CreateInvoice", "Home", null,
             new AjaxOptions { OnSuccess = "showResult" }))

Now the request will be made with Ajax and any returned data will be passed to the showResult JavaScript function. Pretty easy change, but there is one major missing piece. We must include the JavaScript library for this. The JavaScript file for any library needs to be added to the Scripts section of the project and then an entry needs to be added to BundleConfig.cs. Here we added jquery.unobtrsive-ajax.js to the jquery bundle.

     bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
         "~/Scripts/jquery-{version}.js",
         "~/Scripts/jquery-ui-{version}.js",
         "~/Scripts/jquery.unobtrusive-ajax.js"));

We also added the jquery-ui-{version}.js. The framework will figure out which version to fill in from the js files in the scripts folder. This way we can update these without having to change any C# code. If we didn’t add the unobtrusive library then nothing would happen and we wouldn’t get any errors because nothing would be hooked up to fail. When having trouble with JavaScript always check you have the correct libraries added. Similarly we have added the css and images for JQuery to the project. You could put these in script tags in your HTML, but the bundles help with eventual deployment as we’ll see in a later article. These bundles are referenced in the cshtml files to actually include them in the generated HTML and one gotcha is that sometimes the order of including them matters due to namespace conflicts, for instance the bootstrap bundle must be placed before the jQuery bundle or something in JQuery won’t work due to some naming conflicts.

Now we add the showResult function and what it calls.

function showResult(data)
{
    $.fn.showMessageDialog(data.Warnings, data.Errors, data.Messages);
}
(function ($) {
    $.fn.showMessageDialog = function (warnings, errors, messages) {
        $("#infoWarningsBlock").hide();
        $("#infoSuccess").hide();

        if ((warnings != null && warnings.length > 0) ||
            (errors != null && errors.length > 0) ||
            (messages != null && messages.length > 0)) {

            var text = "";
            var i;
            if (messages != null && messages.length > 0) {
                $("#infoSuccess").show();
                for (i = 0; i < messages.length; i++) {
                    text = text + "<li>" + messages[i] + "</li>";
                }
            }
            $("#infoSuccess").html(text);
            text = "";
            if (errors != null && errors.length > 0) {
                $("#infoWarningsBlock").show();
                for (i = 0; i < errors.length; i++) {
                    text = text + "<li>" + errors[i] + "</li>";
                }
            }
            if (warnings != null && warnings.length > 0) {
                $("#infoWarningsBlock").show();
                for (i = 0; i < warnings.length; i++) {
                    text = text + "<li>" + warnings[i] + "</li>";
                }
            }
            $("#infoWarnings").html(text);

            $("#informationDialog").dialog({
                title: "Information",
                modal: true,
                buttons: {
                    Ok: function () {
                        $(this).dialog("close");
                        $(this).dialog("destroy");
                    }
                }
            });
        }
    }

}(jQuery));

Besides setting up the HTML to display, this code mostly relies on the JQuery dialog function to display a nice message/error/warning dialog type box in the Browser using an iFrame.

This routine requires a bit of supporting HTML which we put in _Layout.cshtml file so it can be shared.

    <div id="informationDialog" style="display: none;">
        <ul id="infoSuccess" style="display: none;"></ul>
        <div id="infoWarningsBlock" style="display: none;">
            <div id="infoWarningsHeader">
                <h4>Warnings/Errors</h4>
            </div>
            <ul id="infoWarnings"></ul>
        </div>
    </div>

Notice it has display set to none so it is hidden on the main page.

From the C# code on the server here is the error handler:

        //
        // Simple generic error handler.
        //
        private void MyErrorHandler(Exception e)
        {
            if (session.Errors == null)
            {
                Errors.Add(e.Message);
                Console.WriteLine(e.Message);
            }
            else
            {
                if (session.Errors.Count == 0)
                {
                    Errors.Add(e.Message);
                    Console.WriteLine(e.Message);
                }
                else
                {
                    copyErrors();
                }
            }
        }

        private void copyErrors()
        {
            int iIndex;

            for (iIndex = 0; iIndex < session.Errors.Count; iIndex++)
            {
                switch (session.Errors[iIndex].Priority)
                {
                    case ErrorPriority.Error:
                    case ErrorPriority.SevereError:
                    case ErrorPriority.Security:
                    default:
                        Errors.Add(session.Errors[iIndex].Message);
                        break;
                    case ErrorPriority.Warning:
                        Warnings.Add(session.Errors[iIndex].Message);
                        break;
                    case ErrorPriority.Message:
                        Messages.Add(session.Errors[iIndex].Message);
                        break;

                }
                Console.WriteLine(session.Errors[iIndex].Message);
            }
            session.Errors.Clear();
        }

It separates the errors, warnings and messages. Also the copyErrors method was separated out so it can be called in the case of success, in case there are any warnings or other messages that should be communicated.

The controller now just passes the variables from the model back to the web browser.

        public JsonResult CreateInvoice(Models.CreateInvoice crInvObj)
        {
            ResultInfo results = new ResultInfo();

            results.Messages = crInvObj.Messages;
            results.Errors = crInvObj.Errors;
            results.Warnings = crInvObj.Warnings;

            crInvObj.DoCreateInvoice();

            return Json(results);      
        }

All the work here is done by the Json method which translates all the C# objects into Json objects. It even translates the C# list collections into JavaScript arrays of strings. So for the most part this handles the communication back to the Browser and the JavaScript world.

error2

Summary

We discussed the Sage 300 ERP error reporting architecture and saw how to integrate that into our ASP.Net MVC sample program. Since this was our first use of Ajax and JQuery, we had a bit of extra work to do to set all that up to operate. Still we didn’t have to write much JavaScript and the framework handled all the ugly details of Ajax communications and translating data between what the server understands and what the browser understands.

Update 2015/01/29: Sometimes View calls will return a non-zero return code for special informational purposes. These will cause the API to throw an exception. To handle these you need to catch the exception, check the View’s LastReturnCode property to see what it is and then continue processing. Perhaps a bit of a pain to do this, but it is the exception rather than the rule.