Stephen Smith's Blog

Musings on Machine Learning…

Archive for December 2015

Adding Your Application to the Home Page

with 6 comments

Introduction

We’ve been talking about how to develop Web UIs using our beta SDK. But so far we’ve only been running these in Visual Studio, we haven’t talked about how to deploy them in a production system. With this article we’ll discuss how to add your menus to the home page. Which files need to be installed where and a few configuration notes to observe.

We’ll continue playing with Project and Job Costing, so first we’ll add PJC on to the end of the More menu in the Home Page:

pjc1

As part of this we’ll build and deploy the PJC application so that we can run its UIs in a deployed environment, rather than just running the screens individually in Visual Studio like we have been.

pjc2

The Code Generation Wizard

When you create your solution, you get a starting skeleton Sage.PM.BusinessRepository/Menu/PMMenuModuleHelper.cs. I’m using PM since I’m playing at creating PJC Web UIs, but instead of PM you will get whatever your application’s two letter prefix is. If you don’t have such a prefix, remember to register one with Sage to avoid conflicts with other Sage 300 SDK developers. Similarly, I use Sage as my company, but in reality this will be whatever company name you specified when you created the solution. This MenuModuleHelper.cs file specifies the name of the XML file that specifies your application’s Sage 300 Home Page menu structure. This C# source file is also where you put code to dynamically hide and show your program menu items, so for instance if you have some multi-currency only UIs then this is where you would put the code to hide them in the case of a single currency database (or application).

The solution wizard creates a starting PMMenuDetails.xml file in the root of the Sage.Web project. Then each time you run the code generation wizard it will add another item for the UI just generated. This will produce rather a flat structure so you need to polish this a bit as well as fix up the strings in the generated MenuResx.resx file in the Sage.PM.Resources project. This resource file contains all the strings that are displayed in the menu. Further you can optionally update all the generated files for the other supported languages.

One caveat on the MenuDetails.xml file is that you must give a security resource that the user has rights to or nothing will display. Leaving this out or putting N/A won’t work. One good comparison is that since these are XML files you can see all of Sage’s MenuDetails.xml files by looking in the Sage 300\Online\Web\App_Data\MenuDetail folder. Note that the way the customize screen works, it removes items and puts them in a company folder under these. It will regenerate them if the file changes, but if you have troubles you might try clearing these folders to force them to be regenerated.

Below is a sample XML element for a single UI to give a bit of flavor of what the MenuDetails.xml file contains.

 

  <item>
    <MenuID>PM4001</MenuID>
    <MenuName>PM4001</MenuName>
    <ResourceKey>PMCostType</ResourceKey>
    <ParentMenuID>PM2000</ParentMenuID>
    <IsGroupHeader>false</IsGroupHeader>
    <ScreenURL>PM/CostType</ScreenURL>
    <MenuItemLevel>4</MenuItemLevel>
    <MenuItemOrder>2</MenuItemOrder>
    <ColOrder>1</ColOrder>
    <SecurityResourceKey>PMCostType</SecurityResourceKey>
    <IsReport>false</IsReport>
    <IsActive>true</IsActive>
    <IsGroupEnd>false</IsGroupEnd>
    <IsWidget>false</IsWidget>
    <Isintelligence>false</Isintelligence>
    <ModuleName>PM</ModuleName>
  </item>

Post Build Utility

Now that we have our menu defined and our application screens running individually in debug mode inside Visual Studio, how do we deploy it to run inside IIS as a part of the Sage 300 system? Which DLLs need to be copied, which configuration files need to be copied and where do they all go? To try these steps, make sure you have the latest version of the Sage 300 SDK Wizards and the matching newest beta build.

The Wizard adds a post build event to the Web project that will deploy all the right files to the local Sage 300 running in IIS. The MergeISVProject.exe utility can also be run standalone outside of VS, its a handy mechanism to copy your files. Its usually a good idea to restart IIS before testing this way to ensure all the new files are loaded.

postbuild1

This utility basically copies the following files to places under the Sage300\online\web folder:

  • xml is the configuration file which defines your application to Sage 300. Think of this as like roto.dat for the Web. It defines which are your DLLs to load using unity dependency injection when we start up.
  • App_Data\MenuDetail\PMMenuDetails.xml is your menu definition that we talked about earlier.
  • Areas\PM\*.* area all your Razor Views and their accompanying JavaScript. Basically anything that needs to go to the Browser.
  • Bin\Sage.PM.*.dll and Bin\Sage.Web.DLL are the DLLs that you need to run. (Keep in mind that I’m using Sage for my company name, you will get whatever your company is instead of Sage in these).

With these in place your application is ready to roll.

Update 2016/01/20: This tool was updated to support compiled razor view and the command line is now:

Call "$(ProjectDir)MergeISVProject.exe" "$(SolutionPath)"  "$(ProjectDir)\"
     {ModuleName}MenuDetails.xml $(ConfigurationName) $(FrameworkDir)

Plus it is only run when the solution (not an individual project) is set for a “Release” build.

Compiled Views

When we ship Sage 300, all our Razor Views are pre-compiled. This means the screens start much faster. If you don’t compile them, the when first accessed, IIS needs to invoke the C# compiler to compile them and we’ve found that this process can take ten seconds or so. Plus, the results are cached and the cache is reset every few days causing this to have to happen over again. Another plus is that when pre-compiled the Views can’t easily be edited, which means people can’t arbitrarily change these causing a problem for future upgrades.

Strangely Visual Studio doesn’t have a dialog where you can set whether you want your Views pre-compiled, you have to edit the Sage.Web.csproj file directly. And you need to change the XML value:

<MvcBuildViews>false</MvcBuildViews>

Between true and false yourself in a text editor.

The Sage 300 system is set so that it only runs compiled Razor Views. If you do want to run un-compiled Razor Views, then you need to edit Sage300\online\web\precompiledapp.config and change updatable from false to true.

Beta note: As I write this the MergeISVProject utility doesn’t copy compiled Views properly. This will be fixed shortly, but in the meantime if you want to test with compiled Views you need to copy these over by hand.

New Beta note: This tool now fully supports compiled razor views.

Beta note: The previous beta wouldn’t successfully compile if you were set to use compiled Views, this has been fixed and the solution wizard now includes all the references necessary to do this.

Summary

This article was meant to give a quick overview of the steps to take once you have a screen working in Visual Studio debug mode, to being able to test it running in the Sage 300 Home Page as part of a proper deployment. Generally, the tools help a lot and hopefully you don’t find this process too difficult.

 

Written by smist08

December 29, 2015 at 4:26 pm

Sage 300 Web UI SDK – Adding JavaScript

with one comment

Introduction

Last week we talked about adding UI controls to our Web UI project. In the early days of the Web, this would be all you needed to do. The user would just enter the data and hit save (or submit). However modern Web applications are expected to be more responsive and interactive. This is achieved by adding JavaScript code which can perform local processing or make calls to the server for additional data or to perform additional actions. Most modern Web applications contain quite a bit of JavaScript and achieve quite a high degree of user interaction.

jscalm

In our source code for the new Web UIs, it turns out that 80% of the application code is C# and 20% is JavaScript. Although it is only 20%, this is still a lot of code and care needs to be taken with it. Some programmers really love JavaScript, some really hate it. JavaScript is more of a free form interpreted language that is very forgiving. This allows a lot more freedom in how you structure your programs. Debugging and writing JavaScript is quick and easy since there is no big build/compile step. On the other hand if you capitalize or spell something wrong, there is no compiler to catch the mistake and debugging these errors can be quite frustrating.

We use JQuery to isolate ourselves from Browser differences, for the most part with newer versions of all the Browsers the differences in dialects of JavaScript is much smaller than it used to be. But it still has to be taken into account. Usually the differences manifest themselves in error handling and security concerns. Sometimes one Browser might ignore an error and keep on going whereas another Browser will throw an exception. Similarly, as some practices that lead to security problems are tightened, the implementation can be a bit uneven across the Browsers. Fortunately, most of our application programming doesn’t hit these cases, but it does highlight one the importance of testing in all the supported Browsers.

Another good thing about JavaScript is that tools have all gotten better. You can debug your code in Visual Studio while it runs in Internet Explorer quite seamlessly with debugging you C# code. Further the other Browser all have great debugging tools that can really help. A lot of times you can get several views on something to see what is going wrong. Plus there are compilers for JavaScript to help find some common typos and to enforce programming standards you might want to adopt.

Frameworks

We provide a global framework for doing things like popping up Finders. We also have lots of routines to handle our standard data like dates and numbers. We use a number of open source libraries like JQuery and Knockout.js as well as the (partly commercial) Kendo UI library. Generally, if you know our framework, you don’t need to know that much from the other libraries, but as you get more sophisticated, you will want to leverage them as well. There is also nothing stopping you including further libraries, just that we won’t be able to support you in their use.

Server Independent

We don’t use any libraries with matching client and server components. Hence all communications are handled by our JavaScript code, which means we have full control over all communications. This means it’s our responsibility to not be chatty. It also means we write all the code to handle any Ajax calls from the Browser. ASP.Net MVC makes this pretty easy. It also means that if you want to add your own components (say another UI control) then you can do so as long as you follow this same basic approach.

Example Code

The Web UI code generation wizard creates three JavaScript files for each UI. These are:

  1. screennameKoExtn.js – This is an extension to the knockout data binding where you can add some JavaScript generated calculated fields to the model.
  2. screennameRepository.js – This file contains a method for each RPC call that can be made. We start with the basic CRUD operations, but you would add your screen specific ones here.
  3. screennameBehaviour.js – This is all the other JavaScript code. You may want to break this file up for larger UIs. But for smaller UIs this is a handy place to contain all the JavaScript for working with the controls on your form.

The Behaviour.js file contains a large object for the screen with functions to bind controls to methods and to do various processing when that control is activated. For instance, for our sample PJC CostType UI, in the init routine it will call initButtons which will bind all the buttons to code including the Save button:

       // Save Button
       $("#btnSave").bind('click', function () {
              sg.utls.SyncExecute(costTypeUI.saveCostType);
       });

Then the saveCostType routine is:

saveCostType: function () {
     if ($("#frmCostType").valid()) {
         var data = sg.utls.ko.toJS(modelData, costTypeUI.computedProperties);
         if (modelData.UIMode() === sg.utls.OperationMode.SAVE) {
              costTypeRepository.update(data, costTypeUISuccess.update);
         } else {
         costTypeRepository.add(data, costTypeUISuccess.update);
      }
   }
},

The costTypeRepository.update routine is in the Repository.js file and performs the Ajax call to do the save:

       // Update
       update: function(data, callbackMethod) {
              costTypeAjax.call("Save", data, callbackMethod);
       }

This Ajax.call routine will actually transmit the data model to the server where it is picked up by a controller and processed. We talk about the server side of things in a later article. The entire file is a bit on the long side to include in the blog, but hopefully this give a bit of flavour of what it contains.

The dataModel.UIMode is a predefined calculated field in the screennameKoExtn.js file and keeps track of whether the UI is in Add or Save mode.

Summary

This was a quick glimpse of the JavaScript that is always running behind the scenes in our new Sage 300 Web UIs. Once we cover the server side, we’ll have to consider a lot of cases that go from the HTML to the JavaScript to the C# to the Sage 300 Business Logic to the SQL database and then all the way back. This is just the second step on this journey after last week’s article.

 

Written by smist08

December 12, 2015 at 8:22 pm

Sage 300 Web UI SDK – Adding UI Controls

with 11 comments

Introduction

In my last posting I showed how to quickly create an empty Sage 300 Web UI by running our two new wizards from Visual Studio. In this article we’ll look at how to add some visual controls to this project and talk a bit about some of the issues with doing this, namely about using our provided HTML helper functions and CSS styling.

We’re basically going to continue on and add the visual elements for the PJC Cost Types setup screen. We won’t write any JavaScript yet, so the only functionality will be that provided by the code generator and the default data binding support. This still give quite a bit as you can navigate, use the finder, delete records and save updates.

The UI Wizard discussed last week produces a simple starting page with the standard heading controls, the key field and the Save and Delete buttons. These are all wired up to Javascript and working. This makes our life much easier when adding the rest of the controls.

The only thing you need to do manually is change the Starting Page to: “/OnPremise/PM/CostType” on the Web tab of the Web project’s properties. Then it will compile and run yielding:

costtype1

Adding the Parts

ASP.Net MVC Razor Views are a technique to dynamically generate our HTML by embedding C# code in an HTML template. When the HTML needs to go to the browser the C# code is executed and it usually generates more HTML into the template, so that pure dynamically generated HTML is transmitted to the Browser. The Razor View system is very extensible and it allows a lot of extensibility which we do by adding a large set of helper functions.

Below is the screen once we add some more controls. I showed with a record loaded since that part works with the generated code. The dates and bottom combo box aren’t working yet since we need to add some JavaScript code to help them out.

costtype2

The source code for this screens Razor View (the partial view part) is:

(That didn’t work so well. Apparently WordPress ate all the div’s, I’ll do a bit of research to see if I can fix this, so a bit is missing from the below code. I also added some line breaks so the code doesn’t go off the right of the page).

@* Copyright © 2015 Sage *@
@model Sage.Web.Areas.PM.Models.CostTypeViewModel<Sage.PM.Models.CostType>
@using Sage.PM.Resources.Forms

@using Sage.CA.SBS.ERP.Sage300.Common.Web.AreaConstants
@using Sage.CA.SBS.ERP.Sage300.Common.Resources
@using Sage.CA.SBS.ERP.Sage300.Common.Web.HtmlHelperExtension
@using Sage.CA.SBS.ERP.Sage300.Common.Models.Enums
@using AnnotationsResx = Sage.CA.SBS.ERP.Sage300.Common.Resources.AnnotationsResx

@Html.ConvertToJsVariableUsingNewtonSoft("CostTypeViewModel", Model)

@Html.Partial("~/Areas/PM/Views/CostType/Partials/_Localization.cshtml")
<section class="header-group">
    @Html.SageHeader3Label("CostTypeHeader", CostTypeResx.Entity)
    @if (Model.UserAccess.SecurityType.HasFlag(SecurityType.Modify))
    {
        @Html.KoSageButton("btnNew", null, new { @value = CommonResx.CreateNew, @id = "btnNew",
             @class = "btn-primary" })
    }
    @Html.Partial(Core.Menu, Model.UserAccess)
</section>

<section class="required-group">
    @Html.SageLabel(CommonResx.RequiredLegend, new { @class = "required" })
</section>


  @Html.SageLabel("CostTypeCode", CostTypeResx.CostTypeCode, new { @class = "required" })
  @Html.KoSageTextBoxFor(model => model.Data.CostTypeCode, new { @sagevalue = "Data.CostTypeCode",
      @valueUpdate = "'input'" }, new { @id = "txtCostTypeCode", @class = "default txt-upper",
      @formatTextbox = "alphaNumeric" })
  @Html.KoSageButton("btnLoadCostTypeCode", null, new { @id = "btnLoad", @class = "icon btn-go",
      @tabindex = "-1" })
  @Html.KoSageButton("btnFinderCostTypeCode", null, new { @class = "icon btn-search",
      @id = "btnFinderCostTypeCode", @tabindex = "-1" })
  @Html.ValidationMessageFor(model => model.Data.CostTypeCode)
            
</div>

@* End of generated header, next is code I wrote. *@



    @Html.SageLabelFor(model => model.Data.Description)
    @Html.KoSageTextBoxFor(model => model.Data.Description, new { @value = "Data.Description",
        @valueUpdate = "'input'" }, new { @id = "tbDescription", @class = "large" })
    @Html.ValidationMessageFor(model => model.Data.Description, null)
            
 </div>



   @Html.SageLabelFor(model => model.Data.LastMaintained)
   @Html.KoSageTextBoxFor(model => model.Data.LastMaintained, new {
       @value = "Data.ComputedLastMaintainedDate" }, new { @disabled = "true", @class = "default" })
            


    @Html.KoSageCheckBox("chkStatus", false, new { @sagechecked = "Data.Status" },
         new { @id = "chkStatus" })
    @Html.SageLabel(CommonResx.InactiveAsOfDate, null, new { @for = "chkStatus", @class = "" })
                
    @Html.KoSageTextBox("txInactiveDate", new { @value = "Data.ComputedInactiveDate" },
         new { @disabled = true, @class = "default " })
   </div>
</div>



    @Html.SageLabelFor(m => m.Data.CostClass, new { @id = "lblCostClass", @class = "" })
    @Html.KoSageDropDownList("Data_CostClass", new { @options = "CostClass", @sagevalue =
          "Data.CostClass", @optionsText = "'Text'", @optionsValue = "'Value'" },
          new { @class = "w188" })
            
</div>

@* End of my code, next is the generated footer. *@


<section class="footer-group">
   @if (Model.UserAccess.SecurityType.HasFlag(SecurityType.Modify))
   {
      @Html.KoSageButton("btnSave", new { }, new { @value = CommonResx.Save, @id = "btnSave",
            @class = "btn-primary" })
      @Html.KoSageButton("btnDelete", new { }, new { @value = CommonResx.Delete, @id = "btnDelete",
            @class = "btn-primary" })
   }
</section>
</div>

 

I put comments around the code I wrote so you can see what is generated by the code generation wizard versus the code you add later. Basically this is a mixture of C# code (each line starts with @) and HTML which is in the angle brackets.

There isn’t much layout in this file because this is handled by the CSS. For simple screens like this one there are sufficient styles in the provided Sage standard CSS file that we don’t need to add any CSS. As a result, the HTML is actually fairly simple and really just used to logically group things.

Notice that we use Sage provided extension functions to create all the controls. This provides us with the hooks to provide quite a bit of standard functionality. For instance, we don’t want any hard coded strings in our HTML, otherwise we would force our translators to produce a different copy of the HTML for each language and then we would have to maintain all these files. Here we just use the helper function and it will look up the correct string from the language resource appropriate for the user’s language setting. This also gives us the ability to change the underlying control without changing all the HTMLs. So we can use a different date picker control for instance by changing the code our helper function emits rather than editing each HTML individually. Basically giving us a lot of global control over the behavior of the product.

These helper functions also can setup databinding. Any helper that start with ko will bind the data to the model (more precisely the viewmodel). We used ko since we use knockout.js for databinding which perhaps isn’t the best choice of function naming since again we can change the mechanism in the background without effecting the application code.

Notice there is a partial view called _Localization.cshtml that is included. This provides any localized strings that are needed by JavaScript. So anything referenced in here will be generated in the correct language when the page is loaded.

There is a strange call to “ConvertToJsVariableUsingNewtonSoft” near the top of the file. This is to load a copy of the model into JavaScript during page loading. This means we don’t need to do an initialization RPC call to get the model (Sage 300 View) meta data. Basically the usual empty screen then has the default data and meta data as a starting point.

Summary

This was a quick look at the Razor View part of our Web UIs. This is where the controls and layout are specified. Layout is handled by CSS and data binding is provided to greatly reduce required coding. Next we’ll start to look at the JavaScript that runs behind the scenes in the Browser.

 

Written by smist08

December 5, 2015 at 12:11 am