Stephen Smith's Blog

Musings on Machine Learning…

Posts Tagged ‘sage 300c

Sage Connect 2016

with 4 comments

Introduction

The Sage Connect 2016 conference has just wrapped up in Sydney, Australia. I was very happy to be able to head over there and give a one-day training class on our new Web UIs SDK, and then give a few sessions in the main conferences. This year the conference combined all the Sage Australia/New Zealand/Pacific Islands products into one show. So there were customers and partners from Sage HandiSoft, Sage MicrOpay, Sage One as well as the usual people from Sage CRM, Sage 300, Sage CRE and Sage X3.

The show was on for two days where the first day was for customers and partners and then the second day was for partners only. As a result, the first day had around 600 people in attendance. There was a networking event for everyone at the end of the first day and then a gala awards dinner for the partners after the second day.

A notable part of the keynote was the kick-off of the Sage Foundation in Australia with a sponsorship of Orange Sky Laundry. Certainly a worthwhile cause that is doing a lot of good work helping Australia’s homeless population.

There was a leadership forum featuring three prominent Australian entrepreneurs discussing their careers and providing advice based on their experience. These were Naomi Simpson of Red Balloon, Brad Smith of Braaap Motorcycles and Steve Vamos of Telstra. I found Brad Smith especially interesting as he created a motorcycle manufacturer from scratch.

The event was held at the conference center at the Australian Technology Park. This was very interesting since it was converted from the Eveleigh Railway Workshops and still contains many exhibits and equipment from that era. It created an interesting contrast of 2016 era high tech to the heavy industry that was high tech around 1900.

Sage 300

The big news for Sage 300 was the continued roll out of our Web UIs. With the Sage 300 2016.1 release just being rolled out this adds the I/C, O/E and P/O screens along with quite a few other screens and quite a few other enhancements. Jaqueline Li, the Product Manager for Sage 300 was also at the show and presented the roadmap for what customers and partners can expect in the next release as well.

Sage is big on promoting the golden triangle of Accounting, Payments and Payroll. In Australia this is represented by Sage 300, Sage Payment Solutions and Sage MicrOPay which all integrate to complete the triangle for the customers. Sage Payment Solutions (SPS) is the same one as in North American and now operates in the USA, Canada and Australia.

Don Thomson one of the original founders of Accpac and the developer of the Access-C compiler was present representing his current venture TaiRox. Here he is being interviewed by Mike Lorge, the Managing Director Sage Business Solutions, on the direction of Sage 300 during one of the keynote sessions.

donthom

Development Partners

Sage 300 has a large community of ISVs that provide specialized vertical Accounting modules, reporting tools, utilities and customized solutions. These solutions have been instrumental in making Sage 300 a successful product and a successful platform for business applications. Without these company’s relentless passionate support, Sage 300 wouldn’t have anywhere near the market share it has today.

There were quite a few exhibiting at the Connect conference as well as providing pre-conference training and conference sessions. Some of the participants were: Altec, Accu-Dart, AutoSimply, BSP Software, Dingosoft, Enabling, Greytrix, HighJump, InfoCentral, Orchid, Pacific Technologies, Symphony, TaiRox and Technisoft.

exhibis

I gave a pre-conference SDK training class on our new Web UIs, so hopefully we will be seeing some Web versions of these products shortly.

Summary

It’s a long flight from Vancouver to Sydney, but at least it’s a direct flight. The time zone difference is 19 hours ahead, so you feel it as 5 hours back which isn’t too bad. Going from Canadian winter to Australian summer is always enjoyable to get some sunshine and feel the warmth. Sydney was hopping with tourist season in full swing, multiple cruise ships docked in the harbor, Chinese new year celebrations in full swing and all sorts of other events going on.

The conference went really well, and was exciting and energizing. Hopefully everyone learned something and became more excited about what we have today and what is coming down the road.

Of course you can’t visit Australia without going to the beach, so here is one last photo, in this case of Bondi Beach. Surf’s up!

bondi

Advertisements

Written by smist08

February 25, 2016 at 2:46 am

Sage 300c Web Services

with 16 comments

Introduction

Hand in hand with true HTML/JavaScript/CSS Web UI’s you also want to access the same logic from other general programs using RESTful Web Services. This gives a general API to access the application which doesn’t require any Sage 300 components be installed on the client computer and doesn’t require the calling application be on the same computer or even at the same location.

ASP.Net MVC Web screens tend to have quite quickly changing interfaces between the Views, Controllers and Models which makes using then same Web Services as the UI a bit problematic, especially as the screens evolve quickly. You want a stable Web Services interface that preserves compatibility from version to version and provides a wider more general interface. At the same time the developer of an ASP.Net MVC program doesn’t want to do a completely different implementation for exposing Web Services.

The way ASP.Net MVC solves this dilemma is by allowing you to add a Web Services stack on top of your existing models (which in our case means fully leveraging the business repositories and Sage 300 Business Logic as well). But it uses a custom controller to handle the Web Services requests. In the Microsoft stack there are several supported standards for Web Services, but the one we used is OData. This means that using our Web Services you can do all the standard OData queries and supports the standard OData meta-data.

With our Sage 300 2016 Product Update 1 we have included a number of Web Services in the product. These are automatically installed if you select the Web UIs option from the main installation. So if the Web UIs are up and running then you can try playing with the Web Services. In this article we’ll show how to get started using these. Over next couple of releases, we’ll be fleshing these out to support all the Business logic as well as services beyond the basic CRUD operations.

Some Examples

If you type:

https://yourservername/Sage300webapi/sdata/-/SAMLTD/GL/Accounts

into the Chrome browser you will be prompted for your Sage 300 login credentials, which you can enter. Note that from this browser prompt the password is case sensitive, so you need to uppercase your normal Sage 300 password (since our regular login screen normally does this).

webserv1

Then after entering the correct data you will get back a JSON object with all the information in your chart of Accounts (including details like optional fields):

webserv2

Working with the Browser directly, although fun, will soon become tedious. Another easier approach is to install the Chrome add-in PostMan which will remember your Web Services so you can adjust and repeat them. You need to set the Basic Authorization header with your Sage 300 login and password. Below we use the shortened URL to get the list of all the available feeds for SAMLTD with the URL:

https://yourservername/Sage300webapi/sdata/-/SAMLTD

using PostMan:

webserv3

And we get the returned JSON object containing the list of Web Services we support. It by company since not all accounting application may be activated in the database.

Queries

You can do standard OData queries to filter the returned data. For instance:

https://yourservername/Sage300webapi/sdata/-/SAMLTD/GL/Accounts?$filter=UnformattedAccount eq ‘1020’

will result in just this one account being returned:

webserv4

The way we implement queries is via adding LINQ support that will convert the LINQ query to a Browse filter for our Sage 300 View. This means we will support any query as long as we can translated it into a Browse filter. If the filter contains a SQL function we don’t support, then you will get back a not supported error for your query. Note that often people writing code for the regular Web UIs just use our C# LINQ support to browse/fetch rather than calling browse/fetch directly since this lets you leverage other advanced features in C# and .Net.

Other Clauses

You can specify a sort order as long as what you requests matches an index in the Sage 300 database:

https://yourservername/Sage300webapi/sdata/-/SAMLTD/GL/Accounts?$orderby=UnformattedAccount desc

webserv5

You can specify to get the top n records or to skip n records via:

https://yourservername/Sage300webapi/sdata/-/SAMLTD/GL/Accounts?$top=2

https://yourservername/Sage300webapi/sdata/-/SAMLTD/GL/Accounts?$skip=2

which is useful to page data.

Meta Data

You can get meta data for all the feeds using the $metadata tag. For instance:

https://yourservername/Sage300webapi/sdata/-/SAMLTD/$metadata

will return the meta data for all the feeds that are relevant for SAMLTD:

webserv6

(Note that this is quite a large JSON object to process).

Updating/Inserting/Deleting

This initial implementation includes sufficient G/L feeds for supporting financial reporting. Hence these G/L feeds are read only at this point. We do support inserting G/L Batches, O/E Orders and A/R Customers. Many of the non-G/L feeds support updating, inserting and deleting. If the entity supports these then you can delete the record by specifying DELETE as the HTTP verb (which is easy in PostMan), similarly insert if via POST and update if via PUT or PATCH.

Generally, the best way to figure out the format of the payload to include with these is to do a GET and then use that payload as a template to build the JSON object with the data you want to update or insert.

Since these Web APIs are built on the Sage 300 Business Logic all the usual validation will take place and you will get back Sage 300 error messages in the response payload if the request fails.

Troubleshooting

Ideally the responses from the server will include error messages to tell you what went wrong, so always check these. If they aren’t helpful, then on your web server check the Web API trace log which is located at:

Sage300InstallDir/Online/WebApi/Logs/trace.log

This will usually have the raw error when something has gone wrong.

If you don’t see anything in either of these places, perhaps check your IIS log to make sure that the request didn’t get rejected for some other reason. Especially remember to include your basic authorization header.

Security

If you expose your Web Services to the general Internet, ensure that you follow all the security measures in this article. You will need to do this if you are integrating with an external cloud service or other client located outside your network. Generally, you want to keep your Web Service communications private, so they can’t be accessed by hackers or spied on by hackers. Using good practices around enforcing HTTPS is crucial here.

Summary

The set of Web Services included in the Sage 300 2016 Product Update 1 are intended to support Financial Reporting on General Ledger as well as basic e-Commerce functionality like accessing Customers and entering Orders. Part of the intent of this release is to let people play with these and provide feedback as we move to complete out the full set of Web Services for our next version.

Written by smist08

February 15, 2016 at 11:26 pm

Accessing Sage 300c’s Business Logic from the Web UIs

with 5 comments

Introduction

In the Sage 300 VB UIs, a user would do something in the UI (press a button or tab out of a field) and then the VB UI would be notified of this and would possibly execute a number of Sage 300 Business Logic (View) calls and based on their results update various other fields and possibly provide user feedback via a message box.

In the Web UIs we want to do similar processing since we want to re-use the tried and true Sage 300 Business Logic, but we have to be careful since now the Web UI is half running as JavaScript in the Browser and half running as .Net assemblies on the server. We have to be careful of the communication between the Browser and the server since there will be quite a bit of latency in each call over the Internet. Generally, we never want one user action to result in more than one call to the server (and ideally most user actions shouldn’t result in any calls to the server).

This blog post talks about where you put your code to access the Sage 300 Business Logic and how a UI interaction in the Browser flows through the system to execute this business logic.

Architecture

In the new Web UI architecture, we access the Sage 300 Business logic from our Business Repository classes. The base classes for these provide a wrapper of the Sage 300 .Net API to actually access the Views, but hiding the details of things like session and database link management. Then above this layer are the usual ASP.Net MVC Models and Controllers.

cna2arch

The Sage 300c Web UI Architecture

Generally, we want to put all this logic in the Business Repository so it can be used by multiple higher level clients including the Web UIs, our new RESTful WebAPI and services which are available for other applications to utilize.

Some of the layering is in place ready for additional functionality like customization. We need provide the common interfaces that can act as the basis for programmatic customization by inserting custom modules into the processing flow via Unity Interception.

Moving VB Code

In VB we often make lots of Business Logic (View) calls all interspersed with lots of interactions with various UI controls. This code has to be separated where the Business Logic (View) calls will go in the Business Repository which runs on the server and then the part that interacts with the controls has to move to the JavaScript code running in the Browser. The Business Repository has to provide the necessary data in a single payload which the mode/controller will transport to the Browser for processing.

The easiest way for the repository to transfer data is to have the model provide extra fields for this communication. This way no extra layers need to be involved, the business repository just populates these fields and the JavaScript layers pull them out of the returned JSON object and use them.

But you only want to add so much to the model, since you don’t want it to be too cumbersome to move around and you might want more focused calls. For these we usually define special calls in the controller and these go through a services layer to execute the code in the repository. The service call only passes the exact data needed (like parameters to a function) and knows what data to expect back.

Example

Adding extra fields to the model is fairly straight forward, so let’s trace through the logic of making a services call. In this example we’ll look at the simple case of checking a customer’s credit limit in A/R Invoice Entry (which is using a stateful business repository). We’ll start up in the JavaScript code and work our way down through the layers to get an idea of who does what.

So let’s start near the top. In the A/R Invoice Entry UI there are various times when the credit limit needs to be looked up. So the JavaScript code in the InvoiceEntryBehaviour.js file has a routine to initiate this process. Note that server calls are asynchronous so the response is handled in a callback function.

    showCreditLimit: function (result) {
        // Open Credit Check pop up window
        if (result) {
            var jsonResult = JSON.parse(result);
            if (jsonResult.ShowCreditCheck) {
                arInvoiceEntryRepository.getCreditCheck(jsonResult.id,
                    sg.utls.kndoUI.getFormattedDate(jsonResult.docDate),
                    sg.utls.kndoUI.getFormattedDate(jsonResult.dueDate),
                    "n" + invoiceEntryUI.CurrencyDecimals, jsonResult.totalPaymentAmountScheduled,
                    jsonResult.prepaymentAmount);
            } else {
                onSuccess.onCreditClose();
            }
        }
        invoiceEntryUI.ModelData.isModelDirty.reset();
    },

This calls a function in the InvoiceEntryRepository.js file to actually make the call to the server:

    getCreditCheck: function (customerNumber, documentDate, dueDate, decimals, invoiceAmount,
        prepaymentAmount) {
        var data = {
            id: customerNumber,
            docDate: documentDate,
            dueDate: dueDate,
            decimals: decimals,
            totalPaymentAmountScheduled: invoiceAmount,
            prepaymentAmount: prepaymentAmount
        };
        sg.utls.ajaxPostHtml(sg.utls.url.buildUrl("AR", "InvoiceEntry", "GetCreditLimit"), data,
              onSuccess.loadCreditLimit);
    },

This will initiate the call to the server. The URL will be built something like servername/Sage300/AR/InvoiceEntry/GetCreditLimit. The ASP.Net MVC infrastructure will use configuration by convention to look for a matching entry point in a loaded controller and hence call the  GetCreditLimit method in the InvoiceEntryController.cs file:

        [HttpPost]
        public virtual ActionResult GetCreditLimit(string id, string docDate, string dueDate,
               string decimals,decimal totalPaymentAmountScheduled, decimal prepaymentAmount)
        {
            try
            {
                return PartialView(AccountReceivable.ARInvoiceCreditCheck,
                      ControllerInternal.GetCreditLimit(id, docDate, dueDate, decimals,
                      totalPaymentAmountScheduled, prepaymentAmount));
            }
            catch (BusinessException businessException)
            {
                return JsonNet(BuildErrorModelBase(CommonResx.NotFoundMessage, businessException,
                    InvoiceEntryResx.Entity));
            }
        }

Which will call the InvoiceControllerInternal.cs GetCreditLimit method:

        internal ViewModelBase<CustomerBalance> GetCreditLimit(string customerNumber,
            string documentDate,
            string dueDate, string decimals
            , decimal totalPaymentAmountScheduled, decimal prepaymentAmount)
        {
            var creditBalance = Service.GetCreditLimit(customerNumber, totalPaymentAmountScheduled,
                 prepaymentAmount);

            if (creditBalance.CalcCustomerOverdue == CalcCustomerOverdue.Yes &&
                creditBalance.CustomerBalanceOverdue > creditBalance.CustomerAmountOverdue)
            {
                creditBalance.CustomerCreditMessage = string.Format(
                        InvoiceEntryResx.CustCreditDaysOverdue,
                        creditBalance.CustomerDaysOverdue,
                        creditBalance.CustomerBalanceOverdue.ToString(decimals),
                        creditBalance.CustomerAmountOverdue.ToString(decimals));
            }

            if (creditBalance.CalcNatAcctOverdue == CalcNatAcctOverdue.Yes &&
                creditBalance.NatAcctBalanceOverdue > creditBalance.NatAcctAmountOverdue)
            {
                creditBalance.NationalCreditMessage = string.Format(
                        InvoiceEntryResx.NatCreditDaysOverdue,
                        creditBalance.NatAcctDaysOverdue,
                        creditBalance.NatAcctBalanceOverdue.ToString(decimals),
                        creditBalance.NatAcctAmountOverdue.ToString(decimals));
            }

            creditBalance.CustomerCreditLimit =
                 Convert.ToDecimal(creditBalance.CustomerCreditLimit.ToString(decimals));
            creditBalance.CustomerBalanceVal =
                 Convert.ToDecimal(creditBalance.CustomerBalanceVal.ToString(decimals));
            creditBalance.PendingARAmount =
                 Convert.ToDecimal(creditBalance.PendingARAmount.ToString(decimals));
            creditBalance.PendingOEAmount =
                 Convert.ToDecimal(creditBalance.PendingOEAmount.ToString(decimals));
            creditBalance.PendingOtherAmount =
                 Convert.ToDecimal(creditBalance.PendingOtherAmount.ToString(decimals));
            creditBalance.CurrentARInvoiceAmount =
                 Convert.ToDecimal(creditBalance.CurrentARInvoiceAmount.ToString(decimals));
            creditBalance.CurrentARPrepaymentAmount =
                 Convert.ToDecimal(creditBalance.CurrentARPrepaymentAmount.ToString(decimals));
            creditBalance.CustomerOutstanding =
                 Convert.ToDecimal(creditBalance.CustomerOutstanding.ToString(decimals));
            creditBalance.CustomerLimitExceeded =
                 Convert.ToDecimal(creditBalance.CustomerLimitExceeded.ToString(decimals));
            creditBalance.NatAcctCreditLimit =
                 Convert.ToDecimal(creditBalance.NatAcctCreditLimit.ToString(decimals));
            creditBalance.NationalAccountBalance =
                 Convert.ToDecimal(creditBalance.NationalAccountBalance.ToString(decimals));
            creditBalance.NatAcctOutstanding =
                 Convert.ToDecimal(creditBalance.NatAcctOutstanding.ToString(decimals));
            creditBalance.NatAcctLimitLeft =
                 Convert.ToDecimal(creditBalance.NatAcctLimitLeft.ToString(decimals));
            creditBalance.NatAcctLimitExceeded =
                 Convert.ToDecimal(creditBalance.NatAcctLimitExceeded.ToString(decimals));

            return new ViewModelBase<CustomerBalance> { Data = creditBalance };
        }

This routine first calls the GetCreditLimit service in InvoiceEntryEntityService.cs:

        public virtual CustomerBalance GetCreditLimit(string customerNumber,
            decimal totalPaymentAmountScheduled, decimal prepaymentAmount)
        {
            var repository = Resolve<IInvoiceEntryEntity<TBatch, THeader,
                 TDetail, TPayment, TDetailOptional>>();
            return repository.GetCreditLimit(customerNumber,
                 totalPaymentAmountScheduled, prepaymentAmount);
        }

Who then calls the repository GetCreditLimit routine in InvoiceEntryRepository.cs. This routine then appears to do regular View processing using the base repository wrapper routines that insulate us from the session/dblink handling logic as well as do some basic error processing:

        public virtual CustomerBalance GetCreditLimit(string customerNum,
            decimal totalPaymentAmountScheduled, decimal prepaymentAmount)
        {
            _header.Read(false);
            _creditCheck.SetValue(CustomerBalance.Fields.CustomerNumber, customerNum);
            _creditCheck.SetValue(CustomerBalance.Fields.CurrentARInvoiceAmount,
                totalPaymentAmountScheduled);
            _creditCheck.SetValue(CustomerBalance.Fields.CurrentARPrepaymentAmount,
                prepaymentAmount);
            _creditCheck.Process();
            return _creditCheckMapper.Map(_creditCheck);
        }

Finally, down in the business repository, the code should look fairly familiar to anyone you has done any C# coding using our Sage 300 .Net API. Further this code should also appear somewhere in the matching VB code, and besides being translated to using the .Net API, its become quite separated from the UI control code (in this case the JavaScript).

At the end of this all the calls return propagating the returned data back to the Browser in answer to the AJAX call that it made.

It might look like a lot of code here, but remember the business repository and JavaScript bits have corresponding VB code. Then the other layers are there to make all the code more re-usable so that it can be used in contexts like WebAPIs and allow interfaces to provide the hooks needed for customization.

Summary

This article is intended to give you an idea of where to put your code that accesses the Sage 300 Business Logic and then how to call that from the Web UIs. There are a lot of layers but individually most of the layers are fairly simple and most of the code will appear in the Business Repository and the JavaScript behavior code.

Written by smist08

February 12, 2016 at 3:27 am

Stateless Versus Stateful Sage 300c Web UIs

with 2 comments

Introduction

When two computers communicate they use a well-defined communications protocol. In Browser to Server communications these are often broadly categorized as either stateless or stateful. A stateless communications protocol doesn’t require that anything be remembered between calls. In web applications this is desirable for a number of reasons:

  • often calls are load balanced across multiple servers (perhaps even in different locations).
  • storing state in memory can be expensive and will limit how many users can be accessing a web server at once.

On the other hand, maintaining server state has some advantages:

  • less information needs to be transferred between the client and server since the server knows what has gone before.
  • having things handy in memory can make operations faster and more context aware.

Often in the real world these components are combined or stacked on each other, for instance the TCP protocol is stateful and then the HTTP stateless protocol is layered on top of TCP.

The Sage 300c Web UIs use both stateless and stateful technology. This article will talk about when we use each, how to program either case and what are the advantages and disadvantages of each method. Like many things in programming the lines between these two things can become quite blurred.

Since we do have stateful UIs, this does require that if you scale out to multiple web servers then the load balancer must use sticky sessions as we explained in this blog posting. Basically guarantee that all the requests from a given client go to the same web server in case they are running a stateful UI.

Stateless Sage 300 Web UIs

Most of the Sage 300c setup UIs, processing UIs and report UIs are all stateless. This means IIS could be reset between requests and things would still work. It would just be quite slow since it would need to re-open a Sage 300 session and re-open the Views to process the next request. To avoid this we keep a cache of open sessions along with their open Views. This way when a stateless request comes in we match up a session for the right company, user and with the right Views from the session pool and use that to handle the request. If a session in the pool hasn’t been used for a while we will release it and if too many sessions are in use, we will release the oldest if we need another one. This way practically speaking you usually open a session for your first stateless request and then keep using it from the cache of the duration of your work.

schedules

A Typical Stateless UI

Every Sage 300c business logic entity (View) will have a stateless repository defined for it. This is because only the document entry UI needs stateful operation, everything else like finders, export and other UIs will want to use the stateless version. You get all the default behavior for your stateless repository by having your specific repository inherit from one of the stateless base repositories in Sage.CA.SBS.ERP.Sage300.Common.BusinessRepository.Base like FlatRepository, ProcessingRepository or InquiryRepository.

Generally stateless operation fits very well with ASP.Net MVC since this is the natural way that it works. There is a lot of infrastructure to pass the data model back and forth between the client and server in a stateless manner. Add to that our use of knockout data binding and this makes most basic CRUD operations all handled by the framework.

This doesn’t mean that a stateless UI can’t dynamically interact with the user. By default, you have the CRUD operation off the basic navigation and save/delete buttons. But you can certainly add your own custom AJAX calls to dynamically update areas of your form. We provide support in the framework to update things like descriptions of external keys, but in fact you have a lot of power to make your UI very interactive and polished.

Stateful Sage 300 Web UIs

Most of the main Accounting document entry UIs are stateful. This includes UIs like Order Entry, Purchase Orders, G/L Journal Entry and A/R Invoice Entry. These UIs have a lot of sophistication in building up the Accounting document and the size of these documents can be quite large. Sending this entire document back and forth between the browser and the server as a JSON object is quite impractical. When one of these UIs is started, a session and set of Views are assigned to it for the duration that it runs. As changes are made to the UIs they are sent to the server to make the attendant changes to the server model.

oe1100new

A Typical Stateful UI

To create a stateful business repository, just inherit from one of the stateful base repositories in Sage.CA.SBS.ERP.Sage300.Common.BusinessRepository.Base.Statefull like BatchHeaderDetailRepository or SequencedHeaderDetailRepository.

When you exchange information between the server and browser rather than send the entire document back and forth, you tend to send one component back and forth like one order detail line or the order header. This then reduces the amount of data transmitted, but more importantly greatly reduces the size of the JSON object that JavaScript needs to process. If the transferred document gets too large then JavaScript processing speed can become a real bottleneck.

You don’t need to send something to the server every time every field changes, only the fields that cause some business logic to execute. So you don’t need to do anything say when the user changes a description field. When a field changes that causes business logic on the server to execute then you need to send that to the server and get back all the fields that changed as a result. We tend to mostly do this using the default ASP.Net MVC mechanisms along with the knockout data binding. But only doing it for the component record. So say the item number in a detail record is changed, then we would refresh that record by sending it to the server, which would set the changed fields, do the Sage 300 business logic operations (puts and gets) and return the newly updated record where a number of fields have changed.

This works for most cases, but there is room for optimization. If something is rather a major operation, you might want a more tailored AJAX call for the processing. Similarly, if multiple records are affected you might want to denormalize a bit to reduce the dataflow. For instance, when dealing with details, often header totals change, but you might want to associate these with the detail, rather than also refreshing the header.

Summary

This was a quick overview of the main modes or how our Sage 300 Web UIs operation. Stateless operation reduces server overhead, while stateful operation lets us fully leverage our existing business logic, while reducing our bandwidth requirements.

Written by smist08

February 9, 2016 at 8:44 pm

Sage 300 Web UI Internals

with 10 comments

Introduction

At Sage Summit 2015 we introduced our new Web UIs for Sage 300. I’ve blogged a bit on the various user facing parts and a bit on the technologies used, but I haven’t had a chance yet to get into the internals of how they work. We’ve released the Web UIs for the Financial Modules and will be releasing the Operations Modules early in 2016. With these we will be releasing our SDK as well. Over time I’ll blog quite a bit about the details of all the components, but first we need to layout the major building blocks. Below is the architecture diagram I’ve shared before which is the architecture for the main Web UIs and a block for RESTful Web APIs, but there are some other components that need mentioning as well.

cna2arch

I’m going to start at the bottom of this diagram and work up. However there are some other components that aren’t mentioned here that we will bring in.

Within our framework we provide a lot base classes that you can inherit from, so generally the only code you need to provide is where something is different than the standard protocols or behaviors. When looking for framework components to help you, besides looking for services to call, look for classes that do 90% of what you need that you can extend (inherit from). ASP.Net MVC also makes extensive use of “convention over configuration”, so for a lot of things if you follow the standards then it saves you a lot of work and makes a number of things just work magically. Similarly we do the same things, so we can find and use your components as long as you follow the standards as outlined.

Business Logic

The Sage 300 Views are still the same. We didn’t even update the C View template for supporting this new framework. All the regular Sage 300 Business Logic (Views) is accessed through our .Net API.

Business Repositories

The Business Repositories convert our View API into something more like an ASP.Net MVC API. The Business Repositories are similar to the definitions that we generate from ViewDoc for different systems to make accessing all the various View fields more natural in the given framework. They provide support for all the usual View UI related items like presentation lists and field masks. Generally this is where all the View programming is placed that is needed by the UI. This makes all this programming available to everyone including UIs, standard components and Web APIs.

Models

The models are true ASP.Net MVC Models. They are typically built on a given business repository. The reason for the separation between the Business Repositories and the Models is that the Business Repositories are more general use and can easily be consumed by UI elements whose Models are more generic like for Finders or Import/Export.

State-full Versus Stateless

There are two basic ways that our UIs operate, stateless and state-full. Ideally everything should be stateless, but this becomes somewhat impractical when dealing with large accounting documents. Basically in stateless operation, each RPC operation is completely independent and can be processed with no knowledge of what’s gone before. For state-full transaction a document is built up in the Sage 300 Business Logic as the user enters the document, this is more similar to how the VB UIs work. Generally only the bigger document entry UIs are state-full now. Use of the two is similar and most of the details are hidden by which base class you inherit from (whether a state-full or stateless one), but you must beware of the context as you do your programming.

Controllers

The controllers mediate the translate the RPC calls from the JavaScript components running in the Browser to making calls the Models, Services and other API calls. Sounds like a lot of work, but typically these classes are quite compact.

Services

Often in VB programming, you handle events from the user and as a result of a UI event you do a certain amount of View calls (perhaps say 20 of these). This sort of thing also happens in the Web UI, but we certainly can’t do 20 or so RPC calls to the server as a result of a single UI interaction. This is where services come in. Then when such a UI event is triggered, a single RPC call is made to the server, the controller dispatches this to the service who calls the correct method in the Business Repository which then makes the 20 or so View calls to do the real work. The service then marshals any returned data in the response. Of course this all happens asynchronously. Generally if you are wondering where to move your processing code from a VB UI it would be into one of the Business Repositories and then wire up a service to allow it to be called from the Browser. Services are also used to initiate long running processes which we’ll talk about later.

Views

We use ASP.Net MVC Razor Views for our View components. This is a templating technology that allows us to dynamically generate the HTML using C# (which is embedded in the original HTML). For instance we allow translations to many languages, so rather than having embedded text strings, and then needing a separate copy of the HTML for each supported language, we have embedded C# functions that look up the correct language string which are processed and evaluated when we go to render the HTML. This is very handy and powerful in generating the HTML for each screen based on the various user and application contexts. There is also quite a bit of JavaScript associated with each screen to handle the various dynamic parts of the UI. Much of this is just a matter of wiring up the components like the screen UI elements to data binding or standard UI controls like the Kendo ones to our system JavaScript framework.

Sage.CNA.Windows.Service

We don’t want to run long running processes like I/C Day End or Posting Batches directly from IIS, since even with full multi-threading support this can adversely affect the responsiveness of the UIs for other users. So we run a Windows Service and when a long running process is initiated we ask the Windows Service to run it, and just return from IIS. Then the UI can inquire periodically for meter status updates and to notify the user when it’s done. There is a lot of standard framework support for doing this and you just need to setup a service to initiate and monitor the process. Generally we do this for any Sage 300 process that pops up a progress meter.

KPIs

KPIs are really just like any other UI. They just have a different size and are run in a different place on the Home Page. There are a few standards to follow to match the other KPIs and a few UI controls that aren’t used anywhere else, but are otherwise just standard UIs.

Web API

Although this component isn’t included in the original Sage 300 2016 release, there is full support for exposing RESTful Web Service APIs. These leverage your Model code to expose the model as an API. Then there is some support for hiding inappropriate methods and adding a bit more attribute information to help users.

Printing

Like previous versions of Sage 300, we use Crystal Reports to print out our reports. We basically use the same report API as we used in VB. We then have support to call this API from our UI framework and to render the report in the Crystal HTML Viewer. Then the rest of the report UI is just the same as any other UI, gathering up data from input fields. Printing might also have to initiate a process on the Windows service before printing, like generating the data for an aging report, before initiating the report.

Other System Components

There are system components for things like Finders and Import Export. These just need to be setup and wired in correctly. Then there are components like the editable grid which requires a certain amount more support in your UI. And then higher level components like the Optional Fields Control that is built on the Grid. There are a few other controls and wrappers for things like dates, fiscal year/periods, masked edit control, drop down list based on presentation strings, etc.

Summary

This article is the start of drilling down into the internals of how our new Web UIs work. Hopefully it gives a flavor of the components that combine to make a Web UI. The UIs are fully Ajax Web Applications, so there is work to do both in the Browser in HTML, CSS and JavaScript as well on the server in C#. There are quite a few frameworks involved from Sage, Microsoft, Telerik and a number of open source libraries. The trick is to not be overwhelmed, but to start with a simple empty screen and one by one learn and add the elements that you need. The good thing is that the base classes you extend (inherit from) provide much of the standard code and often you don’t need to add very much at all.

 

Written by smist08

October 17, 2015 at 10:20 pm

International Support in Sage 300c

with 6 comments

Introduction

Sage 300 has always been an international product that is used in many countries around the world. This includes releasing the product in multiple languages, supporting multi-currency, supporting regional settings for things like dates and having flexible configurations for things like sales taxes. As part of the new Web UIs we’ve also carried through all these features into the world of the Web browser. This article describes how some this works, since the browser is a bit different than setting regional settings in Windows.

Generally if you are in a given location, all your computers and users will all be set to use the correct location, so there is nothing for you to do. But if you do want to change things, need to troubleshoot a problem or experiment then these details could be helpful.

Languages and Locales

In the desktop version of Sage 300 you set the language used with the Sage 300 User. Then when you log in as that user, Sage 300 will load the appropriate language files for that user and display in that language. For things like date formats, the desktop product will get them from the Windows regional settings (i.e. the registry) and use that format. In the browser you select a combined language region locale code which determines both the language and the regional settings (like date format). Below is a table for some of these. For the complete list, check out this website.

Sage 300 Language Code Browser Locale Code Description
ENG en English
  en-AU English (Australia) (date format dd/mm/yy)
  en-US English (US) (date format mm/dd/yy)
FRA fr-CA French (Canadian)
ESN es Spanish (not support in Web UIs until Feb release)
CHN zh-Hans Chinese (Simplified)
CHT zh_Hant Chinese (Traditional)

 

I didn’t include South Africa which is en-ZA because the date format for that is yyyy/mm/dd. When I Googled en-ZA, all I got was complaints that it should be dd/mm/yy. So if you are South African and like yyyy/mm/dd as your date format then by all means select en-ZA. However is you prefer dd/mm/yy then you might want to pretend you are Australian and select en-AU. We won’t change our English as a result, so hopefully we can avoid any conflicts about shrimp on the braai vs barbie.

You can set this for any of the Browsers by going into their settings and adding languages and setting which is the current one. For Chrome and Firefox there is a really handy add-on that puts the list in a convenient menu at the top so you can easily toggle between these. In Chrome this is the “Quick Language Switcher”. In Firefox this is the “Quick Locale Switcher”.

Below is G/L Journal Entry in various languages and date formats. Note that data in the database is not translated, which is why there is English text in some of the data fields.

G/L Journal Entry displayed in English (Australian)

G/L Journal Entry displayed in English (Australian)

G/L Journal Entry displayed in French (Canadian)

G/L Journal Entry displayed in French (Canadian)

G/L Journal Entry displayed in Chinese (Traditional)

G/L Journal Entry displayed in Chinese (Traditional)

Configuring the Business Logic

Using the Browser settings affects most things, but some language strings and locale settings come from the Business Logic layer. The Business Logic does get some of its settings from Windows, so to get these in sync with the Browser you should set the Sage 300 application pool in IIS to run as a user that is set to the regional settings that you wish. You also have to change the user that the Sage.CNA.WindowsService Service runs under. By default the application pool and the Sage.CNA.WindowsService service will run as “Local System” which works, but has a few problems:

  1. Local system is very high in security settings, so it’s safer to set this to be a regular user (we don’t require admin rights).
  2. Local system can’t access network resources, so if the shared data folder is on another server you need to change this to a user that can access the shared data folder.
  3. It’s easier to login as a regular user and set their settings to what you need.

You should also ensure that the Sage 300 User has the correct language set since any messages generated by the Business Logic will be in the language of the Sage 300 User.

Hybrid Usage

Most people will still be using the VB screens either due to customizations or modules that we haven’t moved to the web yet. Generally if the Browser, Windows users and Sage 300 users are all set to the same thing then you will get consistent languages and regional settings across everything.

Summary

As Sage 300 moves into the Browser we want to move our international heritage as well. In the web world there is much better support for many of these things and you will see this in our new Web screens.

Written by smist08

September 26, 2015 at 5:25 pm

Skills for Developing for Sage 300c

with 6 comments

Introduction

With the Web UIs in Sage 300c rolling out in a couple of weeks, there is a lot of interest in the SDK and how to develop for this platform. We are still putting together the SDK, but in the meantime you can learn the technologies that are involved in developing our new Web UIs. Generally we’ve used off the shelf components both commercial and open source to develop our new UI framework. The good thing about this is that there are lots of resources available to learn the various technologies involved, including books, web sites, samples, videos, courses, etc.

cna2arch

We’ve generally tried to use all these tools in very standard ways. For instance we don’t add large amount of code to custom controls to change their behavior, we’ve kept it standard and only changed their appearance using CSS. We use the ASP.Net MVC framework in a natural way, so what you learn from the various standard resources is all applicable.

Due to the nature of programming, you can often do quite a few creative things. There is nothing to stop you using other libraries or tools than mentioned here. However one of the points of listing these is to let you know which we use, which means if you ask DPP support, these are the tools and libraries that we know about and can help answer questions about. You are welcome to use other tools, but we may not be able to provide much help on them.

Languages

C#: All server side programming above the Sage 300 .Net API is written in C#. This is a very powerful object oriented extension to C which is quite similar to Java. It has an extensive standard class library. The tools and environments that support C# are really powerful and productive.

JavaScript: All client/browser programming is in JavaScript. We support newer browsers which all now support fairly standard implementations. The main pitfall of JavaScript is that it’s an interpreted language that will process almost anything, often with surprising results.

Framework

ASP.Net MVC: Do not confuse this with ASP.Net (no MVC). This is a completely different framework which is much more powerful and gives a really solid framework for web development.

Web

HTML: HTML controls the general layout of web pages in the browser, however it isn’t as important as it used to be. Our HTML is generated from Microsoft Razor Views, so a good portion of the HTML is actually represented as C# code. Then layout is largely controlled by CSS and not by HTML elements.

CSS: Cascading Style Sheets (CSS) control the layout and look of all the elements on the HTML page. We provide a global stylesheet which has most of what you need. However many screens need to define custom elements for their own fine grained control. We will provide a style catalog and samples of all the main UI elements.

Tools

Visual Studio 2013: This is the main IDE where we develop and debug our code. This is a very powerful and productive environment to write, build and debug code. Chances are we will be on to VS 2015 by the time the SDK ships, but for now this is what we developed our 2016 release in. We use the premium edition because that is what comes with our particular MSDN subscription, but any edition will probably be fine.

ReSharper: This is an optional tool that we’ve licensed for all our developers. We’ve found it very helpful to improve the quality of our code and to help with refactoring.

GitHub: Although using a source code control system is optional, you should be using one. Using any of Git, Subversion, TFS, etc. is fine, but you should really be using one. We use GitHub because it is very fast and reliable. The real benefit is that it’s fast for everyone when you have large internationally dispersed teams.

TeamCity: This is another optional component. You can just build out of Visual Studio. We use TeamCity, but you can also use other automated build systems like Jenkins. Its generally a good practice to have a continuous build/integration system that is always building things as they are developed, deploying them and running automated tests to ensure that things aren’t broken.

Libraries

Kendo UI: This is the library of UI widgets that we use like the editable grid and date control. When we started this project this was strictly a commercial product. However half way through they created the open source Kendo UI Core which has all the controls we use for creating Accounting Screens except the editable grid control. For the grid control and the graphical controls in the KPIs,  you will need to purchase a license for the professional edition.

KnockoutJS: We use knockout for data binding between the UI controls and the MVC models. When we started the project the data binding in Kendo UI didn’t meet our needs so we evaluated alternatives. We found knockout and it did everything we needed so we’ve continued to use it. In the meantime Kendo has improved and AngularJS has become popular, but we’ve stuck with Knockout (which is popular again).

JQuery: Most modern web apps use JQuery. Although its main use of insulating people from browser differences isn’t as important and most browsers have natively implemented its main features, it is still an important library and we use it extensively.

.Net Framework 4.5.1: Since all our server components are written in C# and using the ASP.Net MVC framework, of course we are using the .Net framework. For the 2016 release we are at version 4.5.1. However by the time the SDK ships this will probably be at a higher version.

Sage 300 .Net API: To integrate to the standard Sage 300 Business Logic, we use Sage 300’s .Net API. So when writing code in the MVC models to perform Sage 300 processing, you are writing code to this API.

Crystal Web Viewer: We provide a complete framework for handling Crystal Reports, so you don’t need to directly interact with Crystal. We generate reports using the same code as the desktop version, but then display the result in Crystal’s HTML viewer rather than the ActiveX one.

Unity: This is a library for doing dependency injection in .Net. You probably don’t need to use this directly, but it’s useful to understand how your DLLs are being loaded and why the startup process works like it does.

Summary

This was a quick list of the various tools and technologies we used to create our new Web UIs. Hopefully it gives you a starting point of things to start learning about, if you are interested in Sage 300c development.

Written by smist08

September 19, 2015 at 4:33 pm