Archive for the ‘Customization’ Category
SData Enhancements for Sage 300 ERP 2012
Introduction
I’ve previously blogged on the enhancements for the framework for creating custom SData feeds for applications here and here. In this posting I’m looking at enhancements to our core SData protocol support. We’ve been working hard to add more features and correct some inconsistencies and bugs.
The main purpose of this exercise is to make SData work better for integrators and to make sure the Sage 300 ERP SData feeds work well with other Sage tools like the Sage CRM dashboard and the Sage Argos Mobile Framework.
Global Schema
Generally SData features are mostly of interest to programmers. However some, like this one, enhance existing integrations between different products. Global schema is a mechanism to return all the SData meta-data for a dataset (company) in a single call. In version 6.0A, you could only get the metadata for one SData resource per call. Rather esoteric. But having this enhances our integration to the Sage CRM SData dashboard. Previously when you created an SData widget pointing to an Sage 300 ERP SData feed you needed to specify the $schema for a specific feed, something like:
http://sage300erpdemo.na.sage.com/SDataServlet/sdata/sageERP/accpac/SAMINC/arCustomersFinder/$schema
Now you can give the $schema to the company using an URL like:
http://sage300erpdemo.na.sage.com/SDataServlet/sdata/sageERP/accpac/SAMINC/$schema
Which means you don’t need to know the resource component of the URL. In Sage CRM it looks like this, first you give the URL to the global schema:
Then you get a list of SData resources to pick from in a more human readable form:
Previously you only got the feed you specified. Then you select a feed and hit next to choose the fields you want from that feed.
SData Validation Tool
Sage has written a tool that will validate the correctness of SData feeds. This tool is available here (you need to scroll down to near the bottom of the page). The intent of this tool is for anyone, whether internal or external to Sage to be able to validate any Rest web services for SData compliance against what is described in the spec at the SData Website. This tool was around in the 6.0A days, but it needed work. Back then 6.0A passed the feed validator. With the new tool 6.0A has a lot of problems reported against it. With 2012, quite a bit of work went into making our feed compliant. Which means you can expect them to work as the specification states and integrations with other SData aware products and tools becomes much easier. This tool is continuously being updated and will probably auto-update itself as soon as you install it. Below is a screenshot. Hopefully by release a few of the remaining errors will have been corrected.
Argos
Argos is a framework for creating mobile SData clients using HTML5, JavaScript and CSS. This was originally developed by the Sage SalesLogix group to create the mobile interface for the Sage SalesLogix Mobile product. However since SalesLogix uses SData as its Web Services interface, this library was created entirely on SData. As a consequence it can be used with any product that supports SData.
As part of our Sage 300 ERP 2012 development we tested Argos on our SData feeds and produced a sample mobile application.
As part of this development we fixed a couple of bugs and made sure our SData support works well with the Argos SDK. I’ll write a future blog posting on more details on the Argos SDK and how to write mobile applications for Sage 300 ERP. However if you are interested in Argos, you can check it out, since the whole project is open source and available on github:
- SDataJavaScriptClientLib: https://github.com/sage/SDataJavaScriptClientLib
- argos-sdk: https://github.com/sage/argos-sdk
- argos-saleslogix: https://github.com/sagesaleslogix/argos-saleslogix
- argos-sample: https://github.com/sagesaleslogix/argos-sample
E-Tags
We finished implementing e-tags with this version. These allow proper multi-user control when you have multiple sources updating records. Basically when you read a record, it returns an e-tag which has the date and time the record was last modified. When you update the record this e-tag is included with the update message and then the server can see if the record has been modified by someone else since you read it. This detects the multi-user conflict. Sometimes the server will just merge the changes silently and everyone is happy, sometimes the server will need to send back the dreaded “Record has been Modified by Another User” error response.
Using Detail Fields in Select Clauses
In 6.0A, if you read an SData feed with header and detail components (like O/E Orders), then you got back the header fields and links to the details. Even if you specified detail fields in a select clause. This meant if you wanted both the header and detail lines you needed to make two SData calls. Further this was annoying because it meant the format that you get back reading records is different than how you write the records, so you would need to combine the separate results from the header and details back together to do an update or insert. Now if you specify detail fields in the select clause you will get back all specified fields in the XML package, which will likely be a header with multiple details all returned for the same call. This saves an SData call, but further it’s much easier to deal with, since now you have an already correct XML template for manipulating to do future inserts and updates.
Define Your Own Contracts and Have Your Own Classmaps
In version 6.0A, the only contract we supported for SData feeds created by Sage 300 ERP was the accpac contract. Now in the classmap file you can specify the contract your feeds belong to. This has always been in the classmap files, it just didn’t work. This means you can ensure any feeds you define yourself won’t conflict with anyone else’s.
Another problem in 6.0A was that to create your own feeds, you either needed to be an activated SDK application with your own program id, or you needed to edit one of our existing classmap files. This was annoying since your changes could well be wiped out by a product update. Now you can copy your own classmap file into an existing application (like O/E), you just need to name it classmap_yourownname.xml and it will be added to the defined SData feeds.
Further all the feeds that make up the accpac contract were oriented to the Orion UI work. They weren’t necessarily a good fit for people doing general integration work. So we are creating a new contract which will have the main master files and documents in it that is oriented towards integration work and stateless operation.
Summary
SData continues to be a foundation technology that we are building on. Quite a lot of work has gone into improving our SData support in Sage 300 ERP for the forthcoming 2012 release. This will allow us to leverage many other technologies like Argos to accelerate development.
If you are interested in learning more about SData check out my learning SData videos which are located here and which I blogged about here.
Defining SData Feeds for Sage 300 ERP
Introduction
We introduced SData support with Sage ERP Accpac 6.0A; however, the product as it shipped only defined a few SData feeds that it needed to support the new Web Portal, Data Snapshots, Inquiry Tool and Quotes to Orders features. But Sage 300’s support for SData is based on converting SData Web Service requests into View calls. So it is possible to expose any View (or collection of composed Views) as an SData feed.
In a future version of Sage 300 ERP we will expose all the relevant Views via SData, but in the meantime if you want to use SData with Sage 300, then you need to provide XML files to define the feeds you need.
All the feed definitions are XML files, which means you can read all the existing ones that come with Sage 300 using a normal text editor. Hence you can use the existing ones either as examples or templates for the definitions you need.
One thing to be careful of is that most of the fields in these XML files are case sensitive. This means they must match exactly or they won’t work. When things don’t work, it’s worth looking in the Tomcat\log folder at the relevant SDataServlet.log as this will often point out errors when parsing the XML files.
Class Map Files
The classmap files are a series of XML files located in the sub-folders under: C:\Program Files (x86)\Common Files\Sage\Sage Accpac\Tomcat6\portal\sageERP. The feeds for a given application are stored under the application’s program id and version id directory such as oe60a. Note that these need to be in a folder for an activated application to be read, but within an application you can define feeds that access Views in any application.
All the configuration XML files are loaded into memory by the SDataServlet on startup. So if make any changes to these files, you need to restart the “Sage Accpac Tomcat6” service for your changes to take effect.
You can use these to define custom Java classes to process the SData requests, I’ve covered this a bit in other blog postings, but won’t go into that here, since this article is only considering what can be done by editing XML files.
The classmap defines each SData feed and specifies the class to handle the feed and then a detailed feed definition file called a resourceMap file.
Example – Currency Codes
The currencyCodes resource is implemented by the Java class: ViewResourceKind and is defined by the resource map file: currencyCodeViewMapping.xml. ViewResourceKind is a system provided Java Class for generically converting SData requests into View calls. You can use this to expose most Views (that have data) as SData feeds.
Classmap.xml
<classmap>
<contracts>
<contract name=”accpac”>
<resource name=”currencyCodes” className=”com.sage.orion.sdata.servlet.accpac.ViewResourceKind>
<parameters>
<parameter name=”ResourceMapFile“ value=”currencyCodeViewMapping.xml”/>
</parameters>
</resource>
</contract/>
</contracts>
</classmap>
If you aren’t programming server classes then this is all you need to know about the classmap files.
Resource Map File
Maps an SData resource to a backing family of Accpac Views and fields. These are stored in the resourceMap folder under the folder that holds the classmap file.
By default all fields from the view are exposed as SData Resource Elements.
Has the ability to exclude or include or overwrite Sage 300 fields from the SData resource.
Example – Currency Codes
The currencyCodeViewMapping.xml resource map file contains the following:
<resource name=”currencycode” description=”Currency Codes”>
<viewID>CS0003</viewID>
<pluralName>currencycodes</pluralName>
<includedFields>
<resourceViewField viewFieldName=”CURID” />
<resourceViewField viewFieldName=”CURNAME” name=”Description”/>
<resourceViewField viewFieldName=”DECIMALS” />
<resourceViewField viewFieldName=”SYMBOLPOS” />
<resourceViewField viewFieldName=”THOUSSEP” />
<resourceViewField viewFieldName=”DECSEP” />
<resourceViewField viewFieldName=”NEGDISP” />
<resourceViewField viewFieldName=”SYMBOL” />
</includedFields>
</resource>
The key points are the viewID that maps this feed to the currency codes view CS0003. The URI of the feed is the plural name, namely currencyCodes. Then you can specify the list of fields you want included in the feed. You might specify a shorter list of fields to keep the size of the feed to a minimum. The includedFields section is optional. I tend to prefer using an excludedFields section to just list the fields I don’t want.
Example – Single Level Resource
SData resource “customer” is defined from the view AR0024.
<resource name=”customer” description=”AR Customers”>
<viewID>AR0024</viewID>
<pluralName>customers</pluralName>
</resource>
Example - Multi-Level Resource
SData resource ‘arInvoiceBatch’ is defined from a set of composed views – AR0031, AR0032, AR0033, AR0041 and AR0034.
<resource name=”arInvoiceBatch” description=”AR Batches”>
<viewID>AR0031</viewID>
<pluralName>arInvoiceBatches</pluralName>
<resources>
<resource name=”invoice” description=”AR Invoice”>
<kind>arInvoice</kind>
<viewID>AR0032</viewID>
<resources>
<resource name=”detail” description=”AR Invoice Details”>
<kind>arInvoiceDetail</kind>
<viewID>AR0033</viewID>
<resources>
<resource name=”optional” description=”AR Invoice Detail Optional Fields”>
<kind>arInvoiceDetailOptional</kind>
<viewID>AR0401</viewID>
</resource>
</resources>
</resource>
<resource name=”schedule” description=”AR Invoice Payment Schedules”>
<kind>arInvoiceSchedule</kind>
<viewID>AR0034</viewID>
</resource>
</resources>
</resource>
</resources>
</resource>
Resource Map File Details
Resource Mapping File Attributes / Elements:
name: name of the resource
description: description of the resource.
viewID: The ViewID of the resource. Remember you can get further information on the Views from the Sage 300 ERP Application Object Model (AOM).
pluralName: plural name of the resource. If undefined then the pluralName = name +”s”. This will be the URI of the resource.
resources: Collection of sub-resource elements
kind: resource kind name of the resource. Top resource of the resource tree the resource name and the resource kind name should be the same. However for a sub-resource the resource name reflects the name of the property that refers to it in the parent while the kind name is the name that it appears as at the top level of the schema.
includedFields: list of resourceViewFields that are to be included in the resource (by default all fields are included)
excludedFields: list of resourceViewFields that are not to be included in the resource
overridenFields: list of resourceViewFields that are to be overriden in the resource (usually done to change the SData name)
virtualFields: list of resourceViewFields that are to be added to the resource. Note: virtual fields requires extending ViewResourceKind with a custom class that implements these virtual fields.
lookupFields: These are a 6.1A feature that allow you to add fields looked up from another view like to get a description.
Summary
Hopefully this article gives an idea of how to setup additional SData feeds for Sage 300 ERP, without requiring any programming.
Fun with SData
Introduction
SData is Sage’s new REST based Web Services API used by many of our applications. I’ve blogged about SData a few times already: SData in Sage ERP Accpac 6, More on SData and Sage ERP Accpac 6 and Stateful SData. Calling something a RESTful Web Services API sounds pretty technical and difficult; however, this blog post will try to show that using SData isn’t all that hard and that perhaps it provides and easier to use access method than using SQL or other APIs.
In fact, you might think that you need a complicated SDK with a giant reference manual to use SData. In fact you don’t need an SDK at all. Since SData is based on web standards, many tools already know how to deal with SData. In previous blog posts I showed how SData queries were just URLs that you can just enter in a browser like Chrome and then see the XML response in the browser window. Not very friendly. In this article we’ll show how you can use standard open source tools to play with SData and then how even commercial tools like Microsoft Excel have the capability to deal with SData since Excel knows how to interpret web standards. All the documentation for SData is also freely availalble at http://sdata.sage.com/.
cURL
cURL is an open source command line utility and library. You can use the command line utility to execute SData commands to retrieve data and to insert/update/delete records. cURL is a handy utility to anything you like with things that use the HTTP protocol including requests to web sites and web services.
For instance you can issue the command:
curl –user “ADMIN:ADMIN” http://localhost/SDataServlet/sdata/sageERP/accpac/SAMINC/Customers
which will return all the customer records from the Accpac SAMINC database. The –user parameter is used to set the login, note that these must be in upper case for Accpac. Change localhost with your own server name and replace SAMINC with the company id that you want to use.
You can read a specific record by specifying the index as in the following example that reads good old Ronald Black:
curl –user “ADMIN:ADMIN” http://localhost/SDataServlet/sdata/sageERP/accpac/SAMINC/Customers(‘1200’)
Try playing with the query language specified at http://interop.sage.com/daisy/sdata/Queries/Filtering.html to retrieve just the records you want. For instance:
curl –user “ADMIN:ADMIN” http://localhost/SDataServlet/sdata/sageERP/accpac/SAMINC/Customers?where=IDCUST gt ’1500′
to get customers with codes greater than 1500.
You can also insert records with cURL, for instance issue the command:
curl –user “ADMIN:ADMIN” http://localhost/SDataServlet/sdata/sageERP/accpac/SAMINC/Customers -X POST -T cust.xml
where cust.xml contains:
<?xml version=”1.0″ ?>
<entry xmlns:sdata=”http://schemas.sage.com/sdata/2008/1“
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance“
xmlns=”http://www.w3.org/2005/Atom”>
<id>Customer</id>
<title>Some title</title>
<content>Content</content>
<sdata:payload>
<customer>
<IDCUST>STEVE</IDCUST>
<NAMECUST>Stephen Smith</NAMECUST>
<IDGRP>RTL</IDGRP>
<IDACCTSET>USA</IDACCTSET>
<CODETAXGRP>CALIF</CODETAXGRP>
<IDBILLCYCL>BILLWK</IDBILLCYCL>
<TEXTSTRE1>865 W. 14th Ave.</TEXTSTRE1>
<NAMECITY>Vancouver</NAMECITY>
<CODESTTE>B.C.</CODESTTE>
<CODEPSTL>V5Z 1R2</CODEPSTL>
<CODECTRY>Canada</CODECTRY>
<NAMECTAC>Stephen Smith</NAMECTAC>
<TEXTPHON1>(604) 555-1234</TEXTPHON1>
</customer>
</sdata:payload>
</entry>
Note: With these examples, if you try them and have problems, check the double quotes, Word and blogging software tends to change regular double quotes to the slanted ones which then doesn’t work in XML. Also before user its intended to be two dashes not a long dash.
SData from Excel
Excel has a very good ability to query web sites and then parse the data returned. Excel knows about most web standards including the Atom feeds used by SData. You do this by creating a data connect from the web and specify the SData URL as indicated in the dialog below:
It will prompt you for a user id and passwork, enter your Accpac credentials (all in upper case). There are a lot of extra fields and URLs returned in the worksheet that is produced. But in the middle is all the data that you want from Accpac.
Now you can create a more user friendly worksheet based on this one. You can refresh the data anytime you like and you can automate the collection with a VBA macro. So in a new worksheet you can create charts linked to this data, do calculations on it, or manipulate it any other way you like using Excel.
Finding SData URLs
So how do you know what are the SData feed available from an application? You can run Fiddler to spy on the HTTP traffic to see what requests are being made (similar to other Accpac Spy programs).
But SData is actually fairly self-documenting. For instance if you want to get the list of all SData resources for a given dataset then enter:
curl –user “ADMIN:ADMIN” http://localhost/SDataServlet/sdata/sageERP/accpac/SAMINC
We call this a shortened URL and it returns a feed of all the SData resources that you can use at the next level of the URL. In the same way if you enter:
curl –user “ADMIN:ADMIN” http://localhost/SDataServlet/sdata/sageERP/accpac
it will return to you a feed that contains a list of all the Accpac datasets (companies) on that server.
If you want to get information on a feed you can use a $schema call, so if you issue:
curl –user “ADMIN:ADMIN” http://localhost/SDataServlet/sdata/sageERP/accpac/SAMINC/Customers/$schema
you will get back a description of all the fields in the feed.
Summary
Many people view SData as a developer technology for ISVs to integrate with Sage products. But I think for partners and integrators that are comfortable performing customizations and integrations using SQL, that SData provides another rich approach to consider. I think that especially for newer graduates entering careers installing, customizing and maintaining ERP systems, that URLs and REST based Web Services will be the much more natural way to go.
Customizing Sage ERP Accpac Inquiry
The Inquiry function is a new feature of Sage ERP Accpac 6.0A that adds easy to use Inquiry/Reporting functionality to the new Sage ERP Accpac Web Portal. The Inquiry tool, out of the box, comes with a number of predefined Inquiry templates for G/L, A/R and A/P. A common question is whether you can add to these templates to do Inquiries on additional tables or even additional applications. This blog posting is going to endeavor to show you how to do this. Just a note that this posting is really aimed at developers or partners with knowledge of XML and the database structure of Accpac.
When you work in Accpac Inquiry and customize the totals, the fields displayed or the sorting using the nice easy to use Web Screens, you are editing a template file. This is an XML file that stores all the information about what you are doing in the Accpac Inquiry UI. There are a number of starting templates that come with the system which you edit and then save as your own. You can see these templates under the folder: C:\Program Files (x86)\Common Files\Sage\Sage Accpac\Tomcat6\portal\sageERP\inquiry60a\template. Under template there are language directories since these hold translated strings. Then under each language directory are directories AP001, AR001, GL001 and GL002 for the four categories of templates (see the menu in the picture below).
In the picture the “From Templates” are the templates that come with the system and then Saved are the ones that you have modified.
If you open a template file in a text editor, you will see the query definition:
<QueryDefinition desc=”xxFind customer documents”
textID=”txtARTemplateQryCustomer”
domain-definition-file=”arddf.xml”>
The domain-definition-file (in this case arddf.xml), is the file that defines the data domain definition. This file serves a couple of purposes. It defines all the fields with good descriptions to the Inquiry UI, so users can choose what to select based on good descriptions. It also defines the database structure to the query engine so the data can be returned properly. Some documentation for this file is provided below. Again these are XML files and are stored in the folder: C:\Program Files (x86)\Common Files\Sage\Sage Accpac\Tomcat6\portal\sageERP\inquiry60a\ddf. Since these are XML files, you can open these in a text editor to have a look at them.
If you want to add some fields or tables to an existing data domain, you just need to edit the existing files to get what you are looking for. If you do this, beware that they may be overwritten on the next product update or version, so make sure you have a backup in a safe location (preferably a version control system).
If you want to create a new data domain, then take a copy an existing ddf file and modify it to what you need. Next you need to take a copy of one of the template files and modify it to point to your ddf file. Plus you need to modify anything else in the template file to match what is in the DDF.
One limitation of this process is that you can’t customize the categories in the Inquiry Menu from the Portal. Even if you create a data domain for say Inventory Control, you will still need to place your template in one of the existing categories like “A/R Customers and Transactions”. Hopefully we’ll move this into an XML file in a future version, so it can be customized.
Format of Domain Definition File (DDF)
The Data Domain Definitions files are XML files that define the data universe to Accpac Inquiry.
To facilitate the translation of a DDF file contents and support mixing multiple languages, a DDF file will use the UTF-8 encoding as this format fully supports Unicode. Therefore, the first line of a DDF file should be always defined as follows:
<?xml version=”1.0″ encoding=”UTF-8″?>
XML Document Type Definitions of DDF File
<!DOCTYPE domain[
<!ELEMENT domain (tables, relationships?)>
<!ELEMENT tables (table+)>
<!ELEMENT table (field+)>
<!ELEMENT field (presentation*)>
<!ELEMENT presentation (item+)>
<!ELEMENT item EMPTY>
<!ELEMENT relationships (relationship+)>
<!ELEMENT relationship (join+)>
<!ELEMENT join EMPTY>
<!ATTLIST domain name CDATA #REQUIRED>
<!ATTLIST domain accpac-database-version CDATA #REQUIRED>
<!ATTLIST table name CDATA #REQUIRED>
<!ATTLIST table desc CDATA #REQUIRED>
<!ATTLIST field name CDATA #REQUIRED>
<!ATTLIST field desc CDATA #IMPLIED>
<!ATTLIST field mask CDATA #IMPLIED>
<!ATTLIST item value CDATA #REQUIRED>
<!ATTLIST item string CDATA #REQUIRED>
<!ATTLIST relationship join-type (equal-join|left-outer-join) #REQUIRED>
<!ATTLIST relationship from-table CDATA #REQUIRED>
<!ATTLIST relationship to-table CDATA #REQUIRED>
<!ATTLIST join from-field CDATA #REQUIRED>
<!ATTLIST join to-field CDATA #REQUIRED>
]>
Root Element: <domain>
A DDF’s root element is <domain>. This element has the following attributes and child elements. Every attribute or element is mandatory unless otherwise specified.
Attributes
Name: defines a descriptive name of the domain. This could be displayed to an end user.
accpac-database-version: defines the ERP database version where this DDF is based upon.
For example:
<domain name=”A/R Customer Transactions” accpac-database-version=”6.0″></domain>
Child Elements
<table>
The <table> element has two attributes: name and desc. The name attribute is mandatory, and the desc attribute is optional. If no desc attribute is defined, the physical table name will be displayed to an end user. The only child element of the <table> element is <field>, which defines the fields to be exposed to end user. the <field> element also has two attributes: name, and desc. name and desc define the physical field name in the database and the descriptive name respectively. The desc attribute is also optional.
If a field has a presentation list, it can be defined using the presentation element to map a field’s internal value normally encoded in an integer to a more descriptive string.
For example:
<tables>
<table name=”ARCUS” desc=”Customers”>
<field name=”IDCUST” desc=”Customer Number”/>
<field name=”SWACTV” desc=”Status” >
<presentation>
<item value=”0″ string=”inactive” />
<item value=”1″ string=”active” />
</presentation>
</field>
</table>
</tables>
<releationship>
This element defines the relationship between the tables defined in the <table> section, and it should only be defined if there are more than one table in a domain.
Also, there should be only one tree formed when joining all the tables together in a left-to-right fashion. In other words, there should be only one root table.
We currently support two types of join: equal join and left outer join.
The <releationship> element has three attributes: join-type, from-table, and to-table. It has at least one empty child element <join> which defines the matching fields in the from-table and the to-table. The <join> has two attributes: from-field and to-field.
Below is an example of defining the left outer join between ARCUS and AROBL table:
<releationship join-type=”left outer join” from-table=”ARCUS” to-table=”AROBL”>
<join from-field=”IDCUST” to-field=”IDCUST” />
</releationship>
A Complete Example
Here is an example of A/R Customer Transaction domain. There are 4 tables involved in this domain: ARCUS (customers), AROBL (documents), AROBS (scheduled payments), and AROBP (payments). For simplicity and clarity, not every field in those tables are listed here.
<?xml version=”1.0″ encoding=”UTF-8″?>
<!DOCTYPE domain[
<!ELEMENT domain (tables, relationships?)>
<!ELEMENT tables (table*)>
<!ELEMENT table (field*)>
<!ELEMENT field (presentation*)>
<!ELEMENT presentation (item+)>
<!ELEMENT item EMPTY>
<!ELEMENT relationships (relationship+)>
<!ELEMENT relationship (join+)>
<!ELEMENT join EMPTY>
<!ATTLIST domain name CDATA #REQUIRED>
<!ATTLIST domain accpac-database-version CDATA #REQUIRED>
<!ATTLIST table name CDATA #REQUIRED>
<!ATTLIST table desc CDATA #REQUIRED>
<!ATTLIST field name CDATA #REQUIRED>
<!ATTLIST field desc CDATA #IMPLIED>
<!ATTLIST item value CDATA #REQUIRED>
<!ATTLIST item string CDATA #REQUIRED>
<!ATTLIST relationship join-type (equal-join|left-outer-join) #REQUIRED>
<!ATTLIST relationship from-table CDATA #REQUIRED>
<!ATTLIST relationship to-table CDATA #REQUIRED>
<!ATTLIST join from-field CDATA #REQUIRED>
<!ATTLIST join to-field CDATA #REQUIRED>
]>
<domain name=”A/R Customer Transactions” accpac-database-version=”6.0″>
<tables>
<table name=”ARCUS” desc=”Customers”>
<field name=”IDCUST” desc=”Customer Number” mask=”%12C”/>
<field name=”TEXTSNAM” desc=”Short Name” />
<field name=”SWACTV” desc=”Status” >
<presentation>
<item value=”0″ string=”inactive” />
<item value=”1″ string=”active” />
</presentation>
</field>
<field name=”NAMECUST” desc=”Customer Name” />
<field name=”TEXTSTRE1″ desc=”Address Line 1″ />
<field name=”NAMECITY” desc=”City” />
<field name=”CODESTTE” desc=”State/Prov.” />
<field name=”CODEPSTL” desc=”Zip/Postal Code” />
<field name=”CODECTRY” desc=”Country” />
<field name=”NAMECTAC” desc=”Contact Name” />
<field name=”TEXTPHON1″ desc=”Phone Number” />
<field name=”TEXTPHON2″ desc=”Fax Number” />
</table>
<table name=”AROBL” desc=”documents”>
<field name=”IDCUST” desc=”Customer Number”/>
<field name=”IDINVC” desc=”Document Number”/>
<field name=”DATEDUE” desc=”Due Date”/>
<field name=”DATEINVC” desc=”Document Date”/>
<field name=”TRXTYPETXT” desc=”Document Type” >
<presentation>
<item value=”1″ string=”Invoice” />
<item value=”2″ string=”Debit” />
<item value=”3″ string=”Credit” />
<item value=”4″ string=”Interest” />
<item value=”5″ string=”Unapplied Cash” />
<item value=”10″ string=”Prepayment” />
<item value=”11″ string=”Receipt” />
<item value=”11″ string=”Refund” />
</presentation>
</field>
<field name=”DATEDISC” desc=”Discount Date” />
<field name=”AMTINVCTC” desc=”Customer Currency Invoice Amount” />
<field name=”AMTDUETC” desc=”Customer Currency Amount Due” />
<field name=”AMTDISCTC” desc=”Customer Currency Discount Amount” />
<field name=”SWPAID” desc=”Fully Paid Switch” >
<presentation>
<item value=”0″ string=”Yes” />
<item value=”1″ string=”No” />
</presentation>
</field>
</table>
<table name=”AROBS” desc=”Scheduled Payments”>
<field name=”IDCUST” desc=”Customer Number” />
<field name=”IDINVC” desc=”Document Number” />
<field name=”CNTPAYM” desc=”Payment Number” />
<field name=”DATEDUE” desc=”Due Date” />
<field name=”DATEDISC” desc=”Discount Date” />
<field name=”SWPAID” desc=”Fully Paid Switch” >
<presentation>
<item value=”0″ string=”Yes” />
<item value=”1″ string=”No” />
</presentation>
</field>
<field name=”AMTDUETC” desc=”Original Amount” />
<field name=”AMTDISCTC” desc=”Original Discount” />
<field name=”AMTDSCRMTC” desc=”Remaining Discount” />
</table>
<table name=”AROBP” desc=”Document Payments”>
<field name=”IDCUST” desc=”Customer Number” />
<field name=”IDINVC” desc=”Document Number” />
<field name=”CNTPAYMNBR” desc=”Payment Number” />
<field name=”IDRMIT” desc=”Check/Receipt No.” />
<field name=”DATEBUS” desc=”Posting Date” />
<field name=”AMTPAYMTC” desc=”Cust. Receipt Amount” />
<field name=”IDBANK” desc=”Bank Code” />
<field name=”TRXTYPE” desc=”Transaction Type” />
<field name=”IDCUSTRMIT” desc=”Remitting Customer No.” />
<field name=”DATERMIT” desc=”Receipt Date” />
</table>
</tables>
<relationships>
<relationship join-type=”left-outer-join” from-table=”ARCUS” to-table=”AROBL”>
<join from-field=”IDCUST” to-field=”IDCUST” />
</relationship>
<relationship join-type=”left-outer-join” from-table=”AROBL” to-table=”AROBS”>
<join from-field=”IDCUST” to-field=”IDCUST” />
<join from-field=”IDINVC” to-field=”IDINVC” />
</relationship>
<relationship join-type=”left-outer-join” from-table=”AROBS” to-table=”AROBP” >
<join from-field=”IDCUST” to-field=”IDCUST” />
<join from-field=”IDINVC” to-field=”IDINVC” />
<join from-field=”CNTPAYM” to-field=”CNTPAYMNBR” />
</relationship>
</relationships>
</domain>
Displaying Social Media in the Accpac 6 Portal
The Sage ERP Accpac 6.0A Web Portal provides a nice home page of relevant accounting information to act as a starting point for performing your accounting duties. However it is a web portal and as such it might be nice to display some additional non-accounting information. How to do that? In this blog posting we will see how to add a “Social Media” category to the data snapshots that you can add to your home page and then fill it with a number of useful sites. Plus we will look at how to find and add other useful web pieces. We’ll also do this by adding the proper strings to the database so they can be translated, rather than just hard-coding them in the XML.
Portlet Categories
First we will add the “Social Media” category. This is similar to the categories for the applications already selectable. We need to edit: “C:\Program Files (x86)\Common Files\Sage\Sage Accpac\Tomcat6\portal\swtServices\portletCategoriesMap.xml”. We need to add the following section:
<!-- This is the Social category for Snapshots --> <PortletCategories> <id>SOCIAL</id> <captionID>portal60a_portalMenus_mnuSocial</captionID> <captionString></captionString> <parentID></parentID> <type>2</type> <shortCategoryString></shortCategoryString> <shortCategoryID>portal60a_portalMenus_mnuSocial</shortCategoryID> </PortletCategories>
For a bit more information on XML and customizing see here.
Portlet Map
Now we need to add all the icons to the category (menu) that we have created. To do this we need to edit: ”C:\Program Files (x86)\Common Files\Sage\Sage Accpac\Tomcat6\portal\sageERP\portal60a\portletMap.xml” and add the following items:
<portlet> <id>Social1001</id> <categoryID>SOCIAL</categoryID> <type>1</type> <contentType>1</contentType> <applicationURL>http://www.bbc.co.uk/mobile/i</applicationURL> <captionID>portal60a_social_titlBBC</captionID> <previewID>portal60a_social_prevBBC</previewID> <previewImage>previewimage-GS.png</previewImage> <thumbnailImage>HELP-GS.png</thumbnailImage> <helpID>Portal/index_CSH.htm#110010001</helpID> <showConfigureMenu>false</showConfigureMenu> <showReportMenu>false</showReportMenu> <showRefreshMenu>false</showRefreshMenu> <securityResources></securityResources> </portlet> <portlet> <id>Social1004</id> <categoryID>SOCIAL</categoryID> <type>1</type> <contentType>1</contentType> <applicationURL>http://www.gmodules.com/ig/ifr?url=http://www.gstatic.com/ig/modules/tabnews/tabnews.xml&up_ned=&up_items=8&up_show_image=1&up_font_size=13pt&up_selectedTab=0&up_tabs=&up_last_url=&up_onebox=&synd=open&w=320&h=200&title=__MSG_title__&lang=en&country=ZW&border=%23ffffff%7C3px%2C1px+solid+%23999999</applicationURL> <captionID>portal60a_social_titlGoogleNews</captionID> <previewID>portal60a_social_prevGoogleNews</previewID> <previewImage>previewimage-GS.png</previewImage> <thumbnailImage>HELP-GS.png</thumbnailImage> <helpID>Portal/index_CSH.htm#110010001</helpID> <showConfigureMenu>false</showConfigureMenu> <showReportMenu>false</showReportMenu> <showRefreshMenu>false</showRefreshMenu> <securityResources></securityResources> </portlet> <portlet> <id>Social1005</id> <categoryID>SOCIAL</categoryID> <type>1</type> <contentType>1</contentType> <applicationURL>http://www.gmodules.com/ig/ifr?url=http://hosting.gmodules.com/ig/gadgets/file/105654898094210970759/Social_2_Links.xml&synd=open&w=245&h=350&title=Social+2+Bookmarks+and+Links&border=%23ffffff%7C3px%2C1px+solid+%23999999</applicationURL> <captionID>portal60a_social_titlSocial2Links</captionID> <previewID>portal60a_social_prevLSocial2Links</previewID> <previewImage>previewimage-GS.png</previewImage> <thumbnailImage>HELP-GS.png</thumbnailImage> <helpID>Portal/index_CSH.htm#110010001</helpID> <showConfigureMenu>false</showConfigureMenu> <showReportMenu>false</showReportMenu> <showRefreshMenu>false</showRefreshMenu> <securityResources></securityResources> </portlet> <portlet> <id>Social1006</id> <categoryID>SOCIAL</categoryID> <type>1</type> <contentType>1</contentType> <applicationURL>http://www.gmodules.com/ig/ifr?url=http://www.google.com/ig/modules/finance/finance_related.xml&up_contracted=%2C&up_finance_symbol=GOOG&up_symbol=GOOG&up_finance_symbol_enabled=0&up_finance_rowlinking_enabled=0&synd=open&w=320&h=200&title=__MSG_related_companies__&border=%23ffffff%7C3px%2C1px+solid+%23999999</applicationURL> <captionID>portal60a_social_titlGoogleCompanies</captionID> <previewID>portal60a_social_prevGoogleCompanies</previewID> <previewImage>previewimage-GS.png</previewImage> <thumbnailImage>HELP-GS.png</thumbnailImage> <helpID>Portal/index_CSH.htm#110010001</helpID> <showConfigureMenu>false</showConfigureMenu> <showReportMenu>false</showReportMenu> <showRefreshMenu>false</showRefreshMenu> <securityResources></securityResources> </portlet> <portlet> <id>Social1007</id> <categoryID>SOCIAL</categoryID> <type>1</type> <contentType>1</contentType> <applicationURL>http://www.gmodules.com/ig/ifr?url=http://www.twittergadget.com/gadget.xml&synd=open&w=320&h=400&title=TwitterGadget&border=%23ffffff%7C3px%2C1px+solid+%23999999</applicationURL> <captionID>portal60a_social_titlTwitter</captionID> <previewID>portal60a_social_prevTwitter</previewID> <previewImage>previewimage-GS.png</previewImage> <thumbnailImage>HELP-GS.png</thumbnailImage> <helpID>Portal/index_CSH.htm#110010001</helpID> <showConfigureMenu>false</showConfigureMenu> <showReportMenu>false</showReportMenu> <showRefreshMenu>false</showRefreshMenu> <securityResources></securityResources> </portlet> <portlet> <id>Social1009</id> <categoryID>SOCIAL</categoryID> <type>1</type> <contentType>1</contentType> <applicationURL>http://www.gmodules.com/ig/ifr?url=http://www.notkewl.com/myWeather/myWeather.xml&up_paikka=SFXX0023&up_merkki=1&synd=open&w=280&h=260&title=theWeather&border=%23ffffff%7C3px%2C1px+solid+%23999999</applicationURL> <captionID>portal60a_social_titlWeather</captionID> <previewID>portal60a_social_prevWeather</previewID> <previewImage>igoogle_logo_sm.png</previewImage> <thumbnailImage>HELP-GS.png</thumbnailImage> <helpID>Portal/index_CSH.htm#110010001</helpID> <showConfigureMenu>false</showConfigureMenu> <showReportMenu>false</showReportMenu> <showRefreshMenu>false</showRefreshMenu> <securityResources></securityResources> </portlet> <portlet> <id>Social1010</id> <categoryID>SOCIAL</categoryID> <type>1</type> <contentType>1</contentType> <applicationURL>http://www.gmodules.com/ig/ifr?url=http://www.google.com/ig/modules/facebook.xml&up_session=&synd=open&w=320&h=300&title=__MSG_title__&lang=en&country=ALL&border=%23ffffff%7C3px%2C1px+solid+%23999999</applicationURL> <captionID>portal60a_social_titlFacebook</captionID> <previewID>portal60a_social_prevFacebook</previewID> <previewImage>previewimage-GS.png</previewImage> <thumbnailImage>HELP-GS.png</thumbnailImage> <helpID>Portal/index_CSH.htm#110010001</helpID> <showConfigureMenu>false</showConfigureMenu> <showReportMenu>false</showReportMenu> <showRefreshMenu>false</showRefreshMenu> <securityResources></securityResources> </portlet> <portlet> <id>Social1011</id> <categoryID>SOCIAL</categoryID> <type>1</type> <contentType>1</contentType> <applicationURL>http://www.gmodules.com/ig/ifr?url=http://www.google.com/uds/modules/elements/localsearch/localsearch.xml&up_location=johannesburg&up_largeMapMode=1&up_kml=0&up_traffic=&up_locationCacheString=&up_locationCacheLat=&up_locationCacheLng=&up_mapType=m&up_idleZoom=11&up_transitionQuery=&up_rawquery=&up_selectedtext=&synd=open&w=320&h=375&title=__MSG_title__&lang=all&country=ALL&border=%23ffffff%7C3px%2C1px+solid+%23999999</applicationURL> <captionID>portal60a_social_titlGoogleMaps</captionID> <previewID>portal60a_social_prevGoogleMaps</previewID> <previewImage>previewimage-GS.png</previewImage> <thumbnailImage>HELP-GS.png</thumbnailImage> <helpID>Portal/index_CSH.htm#110010001</helpID> <showConfigureMenu>false</showConfigureMenu> <showReportMenu>false</showReportMenu> <showRefreshMenu>false</showRefreshMenu> <securityResources></securityResources> </portlet> <portlet> <id>Social1012</id> <categoryID>SOCIAL</categoryID> <type>1</type> <contentType>1</contentType> <applicationURL>http://www.gmodules.com/ig/ifr?url=http://www.google.com/ig/modules/youtube_igoogle/v2/youtube.xml&up_channel=&up_channel_url_to_preload=http%3A%2F%2Fgdata.youtube.com%2Ffeeds%2Fapi%2Fstandardfeeds%2FUS%2Frecently_featured%3Falt%3Djson&up_current_channel_id=0&up_history=&up_historyEnabled=true&up_prefs_version=0&up_rawQuery=&up_searchTerm=&up_search_channel_name0=&up_search_channel_name1=&up_search_channel_name2=&up_search_channel_url0=&up_search_channel_url1=&up_search_channel_url2=&up_search_channels=0&up_showPromo=true&up_title=YouTube&up_userHasSeenSharedActivities=false&up_username=&synd=open&w=320&h=375&title=__MSG_title__&lang=en&country=UK&border=%23ffffff%7C3px%2C1px+solid+%23999999</applicationURL> <captionID>portal60a_social_titlYoutube</captionID> <previewID>portal60a_social_prevYoutube</previewID> <previewImage>previewimage-GS.png</previewImage> <thumbnailImage>HELP-GS.png</thumbnailImage> <helpID>Portal/index_CSH.htm#110010001</helpID> <showConfigureMenu>false</showConfigureMenu> <showReportMenu>false</showReportMenu> <showRefreshMenu>false</showRefreshMenu> <securityResources></securityResources> </portlet>
UI Content
For the translatable strings used in the previous files you need to create one file and edit another. The first is social_uiContent.xml that goes in the: “C:\Program Files (x86)\Common Files\Sage\Sage Accpac\Tomcat6\portal\swtServices\uiContent\portal60a\eng” folder. Create the file with the following contents:
<?xml version="1.0" encoding="utf-8" standalone="no"?> <uiContentList xmlns="http://schemas.sage.com/swtServices" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schemas.sage.com/swtServices/uiContent.xsd"> <uiContent id="titlBBC" status="approved" value="BBC"/> <uiContent id="titlYammer" status="approved" value="Yammer"/> <uiContent id="titlLinkedin" status="approved" value="LinkedIn"/> <uiContent id="titlTwitter" status="approved" value="Twitter"/> <uiContent id="titlGoogleNews" status="approved" value="Google News"/> <uiContent id="titlGoogleCompanies" status="approved" value="Google Companies"/> <uiContent id="titlSocial2Links" status="approved" value="Social 2 Links"/> <uiContent id="titlWeather" status="approved" value="Weather"/> <uiContent id="titlFacebook" status="approved" value="Facebook"/> <uiContent id="titlGoogleMaps" status="approved" value="Google Maps"/> <uiContent id="titlYoutube" status="approved" value="YouTube"/> <uiContent id="menuPortal" status="approved" value="Social Media"/> <uiContent id="prevBBC" status="approved" value="View BBC News on your Sage ERP Accpac Portal."/> <uiContent id="prevYammer" status="approved" value="Stay in touch with your co-workers on Yammer using your Sage ERP Accpac Portal."/> <uiContent id="prevLinkedin" status="approved" value="Connect with others on LinkedIn using your Sage ERP Accpac Portal."/> <uiContent id="prevTwitter" status="approved" value="View tweets about Sage ERP Accpac."/> <uiContent id="prevGoogleNews" status="approved" value="View news provided by Google."/> <uiContent id="prevGoogleCompanies" status="approved" value="Research company profiles."/> <uiContent id="prevSocial2Links" status="approved" value="Links to Social Media networks."/> <uiContent id="prevWeather" status="approved" value="Shows weather reports of your area."/> <uiContent id="prevFacebook" status="approved" value="Connect with your friends in the Sage ERP Accpac Portal."/> <uiContent id="prevGoogleMaps" status="approved" value="View Google Maps in the Sage ERP Accpac Portal."/> <uiContent id="prevYoutube" status="approved" value="Watch YouTube videos in the Sage ERP Accpac Portal."/> </uiContentList>
Now we need to edit portalMenus_uiContent.xml located in: “C:\Program Files (x86)\Common Files\Sage\Sage Accpac\Tomcat6\portal\swtServices\uiContent\portal60a\eng”
Add the line:
<uiContent id="mnuSocial" status="approved" value="Social Media"/>
With the other entries.
Graphic File
Save the following graphic:
As igoogle_logo_sm.png in the folder: C:\”Accpac install location”\Accpac\WebUIs\images.
Give it a Try
Before these changes will work you need to delete the UIContent table from your Portal database. This will cause it to be re-loaded the next time you run the Accpac Portal. When it reloads, it loads all the xml files in the uiContent directories into this table including the ones we created and modified above.
With these customizations in place you should get a Social Media menu when you add a snapshot to your workspace:
How to Add More URLs
Given this framework, it’s easy to expand it to include different URLs. But where do you get URLs for web sites that will fit nicely in these data snapshot windows? First if you can’t find a small version of a web site you want to include, if you make the type in the portletmap.xml, a 2 rather than a 1, then the item will appear in the task menu and you can add it to the shortcuts, rather than the home page workspace.
Generally there are three possible ways to find a small version of a web site: a mobile version, a mashup version or a portlet version (like an iGoogle widget). It’s a matter of trial and error to see which works best. All the examples included here are iGoogle widgets. So how do you get the URL for an iGoogle Widget?
Go to iGoogle and choose “add stuff”, select a widget to add, then press “embed this gadget”. Now configure it how you want and then press the “Get the Code” button. This will give you something like:
<script src="http://www.gmodules.com/ig/ifr?url=http://www.google.com/ig/modules/datetime_v2/datetime_v2.xml&up_color=blue&up_dateFormat=wmd&up_firstDay=0&up_24hourClock=1&up_clocks=&synd=open&w=320&h=148&title=__MSG_title__&lang=en&country=ALL&border=%23ffffff%7C3px%2C1px+solid+%23999999&output=js"></script>
Now to use it in the portal you need to trim off the front: <script src=” and the back: &output=js”></script>. This middle part is then the URL you can use in the portletmap.xml to activate the widget.
Not all iGoogle widgets will work. Some require certain container behavior that iGoogle provides, but that we don’t provide yet. It just takes a bit of trial and error.
If you browse to a web site on your iPhone or Android and it looks ok, make note of the URL that website is using. Usually something like m.usualURL.com and use that in the Portal. Often this will give a nice small version of the website.
Often services like twitter have a page like http://twitter.com/about/resources/widgets that help you embed their service in your website. From these pages you configure the widget and when done click a “Generate Code” button which generates html code for to display their site correctly. You can’t embed this HTML directly in the portletmap.xml file, but you can save it somewhere under Accpac\WebUIs, and then refer to this in the portletmap.xml (remember http://hostname/SageERPAccpac points to Accpac\WebUIs).
Summary
Hopeful this article provides some useful information on how to incorporate information from other sources into the Sage ERP Accpac 6 Web Portal. If future versions of Accpac we should be including this in the normal UIs for customizing the Portal rather than resorting to XML.
Customizing the Order Details in Quote to Orders
The new Order and Quote Entry screens in the Sage ERP Accpac 6 Quote to Order feature are the first real accounting document entry web based screens produced in the new “Orion” technology. As these go to final release there is a lot of interest in how to customize these screens. In the previous blog posting http://smist08.wordpress.com/2010/11/28/customizing-quotes-to-orders/ we started to talk about how to customize these screens. We discussed the declarative layout XML file that describes the layout and performed some simple edits to these screens. In this blog posting we are going to continue customizing these screens, this time customizing the Order Detail Line Table/Grid controls. In this blog posting, we will look at the XML that defines this table along with a couple of customizations.
For more background on Accpac’s new customization model and the XML Declarative layout files see: http://smist08.wordpress.com/2010/02/05/sage-erp-accpac-6-customization/ and http://smist08.wordpress.com/2010/05/28/how-to-layout-forms-in-sage-erp-accpac-6/.
Definition of a SwtTable
The XML file is: C:\”Program Files (x86)\Common Files\Sage\Sage Accpac”\Tomcat6\portal\swtServices\uiDefinitions\oe60a\eng\sagecrmorderui\SageCRMOrderUIUIDefinition.xml. Below is part of the definition of the table control (a widget of type SwtTable) from the declarative layout (many columns omitted):
<widget type=”swt:SwtTable” id=”orders_DETAILS” datasourceID=”oeorderdetails” preferencestoreID=”DetailTableSettings” height=”250″ width=”980″ autoLoad=”false” showRecordNumber=”true” showCustomFields=”true”>
<columnHeaders>
<item propertyBinding=”LINETYPE” dataType=”Int” width=”100″>
<text>
<transText text=”xType” textID=”oe60a_sagecrmorderui_colType”/>
</text>
</item>
<item propertyBinding=”CALC_ITEMNO_MISCCHARGE” dataType=”Char” width=”160″ formatPattern=”[:printupper:]{0,24}”>
<text>
<transText text=”xItem No./Misc. Charge” textID=”oe60a_sagecrmorderui_colItemMisc”/>
</text>
</item>
<item propertyBinding=”DESC” dataType=”Char” width=”200″>
<text>
<transText text=”xDescription” textID=”oe60a_sagecrmorderui_colDesc”/>
</text>
</item>
…
</columnHeaders>
</widget>
The “<widget type=”swt:SwtTable” …” tag defines the SwtTable, sets its initial size along with some other global attributes. You can change these, but might not want to, for instance setting autoLoad to true would cause the table to load when it’s initialized, but the program will load it programmatically so the end effect will be to load it twice. The main thing you will want to customize is the collection of columnHeaders which define all the columns that are displayed in the SwtTable.
Between the <columnHeaders> and </columnHeaders> tags are a number of <item> tags which each define a column in the table. Each item has a number of attributes, the possible attributes are:
- propertyBinding: field in the SData feed that the column is bound to.
- dataType: type of the column possible values are: string, Bool, Byte, Char, Date, Decimal, Int, Long, Real, Time and Object.
- Width: default width of the column.
- readOnly: set to true or false whether the column is read only (versus editable).
- hyperlink: set to true or false whether the column is a hyperlink.
- Sorting: set to true or false whether you can sort of this column.
- formatPattern: format pattern for the column.
- fractionDigits: number of decimals.
- totalDigits: total number of decimal digits.
Some attributes are only valid for certain data types, for instance fractionDigits only makes sense for Decimal and Real types.
Then there is a <text> sub-element inside the <item> tag where you can specify either the text for the column heading or the textID of a string identifier in the uiContent table on the server. The textID will get the appropriate text for the language the user signed in on as. Make sure you set the textID to “” (empty string) if you want to hard code the string in the declarative layout. Editing the strings in the uiContent table in the Portal database is also a good way to customize strings, or to even specify new strings.
Adding a Column
To add a column to the SwtTable we need to add another <item> to the <columnHeaders> collection. For instance if we wanted to add the “Non-stock Clearing Account” to the table then we would add the following to the collection of <columnHeaders> just before the </columnHeaders> tag:
<item propertyBinding=”GLNONSTKCR” dataType=”Char” width=”80″ readOnly=”true” visible=”true”>
<text>
<transText text=”Non-stock Clearing Account” textID=”"/>
</text>
</item>
Even though we’ve now added the field GLNONSTKCR to the SwtTable it wouldn’t display since it isn’t in the O/E Orders SData feed. So we need to add it to the SData feed. To do this we need to edit another XML file, namely: C:\Program Files (x86)\Common Files\Sage\Sage Accpac\Tomcat6\portal\sageERP\oe60a\resourceMap\OEOrderViewMapping.xml. This file among other things defines which Order fields to include in the SData feed. In the <includedFields> section for the detail, add the line:
<resourceViewField viewFieldName=” GLNONSTKCR” />
This will add the GLNONSTKCR feed to the detail. Make sure you add this to the includeFields for the detail and not one of the header. The field names used in the SData feeds is exactly the same as the field names specified in the Accpac Object Model (AOM) located at: https://partners.sagenorthamerica.com/irj/go/km/docs/sageKM/Sage%20Accpac%20ERP/Assets/AOM/Advantage.HTM.
Whenever you change one of the SData definition files, you need to restart Tomcat to have it take effect. To do this: run the Service applet from the Administrative Tools icon in the Control Panel and restart the “Sage Accpac Tomcat 6” service. Or do it from the “Configure Tomcat Service” icon under “Sage Accpac” under the start menu.
Note that is you are using sample data then there are a lot of optional fields defined for the Order details. With this new screen all the optional fields appear in columns of the table rather than from a popup form. This means that when we added our new field it won’t appear as the last column in the table, but somewhere in the middle since all the optional fields will come after it.
When the user customizes the column widths, order and visibility of table columns, these customizations are stored in the PreferenceStore table in the Portal database. If you have played with the columns in the table previously and the new column doesn’t show up, you might want to delete the SageCRMOrderUIUIDefinition-DetailTableSettings row from this table to clear the preferences. This table stores what we used to store in the *_p.ism files from our VB UIs.
Also make sure you have a backup of your customized declarative XML file, since you wouldn’t want future product updates to overwrite your one copy.
Below we see a screen shot of the table with our new column added between the Instructions and the Backorder optional field:
Summary
Hopefully this gives an idea of a few things you can do with the SwtTable that displays and edits the Order/Quote Detail Lines in the new Quotes to Orders. This article showed how to do this by editing the XML directly; but, if you have the SDK then you can use the SwtUIDesigner to do this visually.
Customizing Quotes to Orders
With Sage ERP Accpac 6.0A we have incorporated Web based versions of the Accpac Order and Quote entry screens into SageCRM. These are the first accounting document entry screens in the new Accpac Sage Web Toolkit (SWT) technology. A common question I get asked is how to customize these screens. Certainly the regular current Accpac Order Entry screen is one of the most customized screens in the Accpac system. However it is very hard to customize the current screen when it’s run from SageCRM because of the way it is packaged and run from a CAB file. The new screens are regular Accpac screens and run inside SageCRM just like they would run from Accpac. This means all the regular customization techniques for the new web based technology can be used on them.
As a starting point the screen definitions are now stored in a separate XML screen definition files. You can accomplish a lot just by editing these files. In this blog posting we will look at a collection of simple but powerful customizations you can perform here. In this blog posting we will just be looking at the XML for the controls and manipulating those. Actually in addition to that you can add JavaScript processing code to the events that get fired, this is a bit more sophisticated and we will look at that in a following blog posting.
The XML file is located in the folder: C:\”Program Files (x86)\Common Files\Sage\Sage Accpac\Tomcat6\portal\swtServices\uiDefinitions\oe60a\eng\sagecrmorderui”. The file we will look at is SageCRMOrderUIUIDefinition.xml. The XML files for a couple of popup forms are here also and you can customize these also. You can edit these files in any text or XML editor. If you have the SDK you can edit these files in a visual SWT UI designer, however for the purposes of this blog posting we will assume you don’t have the SDK and edit this file directly as a text file. One nice thing about the new web based technologies is that they are entirely configured by XML files, which being text files can easily be edited for customization purposes.
Introduction to XML
First just a few notes about XML. It’s worth your while to check out a site like http://en.wikipedia.org/wiki/XML to learn the basics of XML. But for what we’re doing here you really don’t need to know very much, just how to find things, cut and paste things, and make minor changes.
The key point is that all data is contained between a starting tag and a closing tag like:
<caption>This is a caption</caption>
Where the closing tag is the opening tag with a / in front of it.
Sometimes you can express attributes for a tag like:
<caption font=”Sans Serif”>This is a caption</caption>
Also if there is no general data then you can end the tag with a /> like:
<caption font=”Sans Serif” text=”This is a caption”/>
Generally most of the other stuff you find in the XML file, you would leave alone, like the version and encoding and such.
Form Layout
We talked a bit on how to layout forms in http://smist08.wordpress.com/2010/05/28/how-to-layout-forms-in-sage-erp-accpac-6/. So we won’t talk too much about how to control the layout here, but we may look at this more in depth in a future posting. But beware that there are two types of widgets in a screen definition file, there are layout widgets that control how things look and then data widgets for displaying/editing data.
Widget Elements
Here a technical description of widgets in the layout XML files taken from the reference XSD file. An XSD file defines what is correct in a corresponding XML file
A widget element must contain a “type” attribute (corresponding to the namespace-qualified class name of the widget). It may contain other attributes such as an ID as well as other attributes corresponding to simple properties (such as “enabled”).
A widget element may also contain other elements that correspond to compound properties. These compound properties include action (for predefined actions associated with button-like widgets), translatable display string properties such as caption, column headers (for tables), images (which include the URLs to the normal, normal, mouseover, and disabled images for a widget), search fields (for finders) and selectable items (for list boxes).
A widget element may also contain a “handlers” element for JavaScript and default actions widget event handling. That “handlers” element in turn contains a list of “handler” elements. Each “handler” element has an event and action attribute that refers to the name of the event and the name of the action that will be executed when the event is triggered. The “handler” element may also contain a “params” element, which in turn contains a list of “param” elements. Each “param” element has an id and value attribute that refers to the name and value of a parameter that will be passed in as an additional parameter to the action that is executed when the event is triggered. Parameters will be passed to the action in the same order in which they are specified in the XML (which is important if it is a java script function being executed by the action).
A “regular” container (“HasWidgets”) widget’s element contains a “children” element. That “children” element in turn contains a list of widget elements for child widgets.
A grid-type container (“HasWidgetsInGrid”) widget’s element contains a “rows” element. That “rows” element in turn contains a list of “row” elements, each of which contains a list of “cell” elements. Each cell is either empty or contains one (child) widget element.
A dock panel widget’s element contains a “dockedWidgets” element. That “dockedWidgets” element in turn contains a set of docking position elements (e.g. “northWidget” element) for north, south, east, west, and center (in that order). Each docking position element is either empty or contains one (child) widget element.
A menu bar or menu button widget’s element contains a “menuItemList” element. That “menuItemList” element in turn contains a list of “menuitem” elements, which in turn may contain other “menuItemList” elements (representing submenus).
Before
Below is a screen shot of the Order Entry screen in CRM as it ships un-modified in the product:
Editing the Layout
Within the layout XML files are widget definitions of the form:
<widget type=”swt:SwtTextBox” id=”orders_ORDNUMBER” enabled=”true” datasourceID=”oeorders” propertyBinding=”ORDNUMBER” width=”170″ style=”swt-TextBox-nohint-noBackground”/>
This is the contents for the base structure of a widget element. These are the elements we customize.
Changing a Caption
Suppose we want to change the Document Number caption in this form. We can find the XML tag for this widget in the XML file:
<widget type=”swt:SwtLabel” style=”swt-Label documentDetails-LeftRow”>
<text>
<transText text=”xDocument Number:” textID=”oe60a_sagecrmorderui_lblDocNumber”/>
</text>
</widget>
This references a nice translatable string that exists in the Portal database, but we want to change this to a hard coded string specific for our customer. If we blank out the textID field, then it will use the text specified in the text field, so we can change the above to:
<widget type=”swt:SwtLabel” style=”swt-Label documentDetails-LeftRow”>
<text>
<transText text=”Customer Specific Reference:” textID=”"/>
</text>
</widget>
Once we save this change, then running the Q2O Order Entry screen will show this caption instead of the one from the Portal database.
Adding a Field
The Quote to Order screen is optimized to make it easy for salespeople to enter orders quickly. However suppose the form is too simple and you require your sales people to enter another field that isn’t already on this screen. In the Q2O screen there is the order description field, but no order reference field. So let’s add the reference field to the Document Details tab.
To do this, find the SwtSingleTabPanel with transText: oe60a_sagecrmorderui_pnlDocDetails. A couple of lines below this is a SwtGridPanel with a rowCount of 4, change this rowCount to 6 (since we are adding one row with the label and one with the textbox.
Then add a new <row> at the end of the grid, this will be just before the </rows> tag:
<row>
<cell>
<widget type=”swt:SwtLabel”>
<text> <transText text=”Reference:” textID=”"/> </text>
</widget>
</cell>
</row>
<row>
<cell>
<widget type=”swt:SwtTextBox” id=”orders_REFERENCE” datasourceID=”oeorders”
propertyBinding=”REFERENCE” width=”400″/>
</cell>
</row>
This will add the controls to the layout. However it won’t work quite yet. This is because the REFERENCE field for the Order Header isn’t provided in the SData feed. However we can add it. To do this we need to edit another XML file, namely: C:\Program Files (x86)\Common Files\Sage\Sage Accpac\Tomcat6\portal\sageERP\oe60a\resourceMap\OEOrderViewMapping.xml. This file among other things defines which Order fields to include in the SData feed. In the <includedFields> section for the header, add the line:
<resourceViewField viewFieldName=”REFERENCE” />
This will add the REFERENCE feed to the header. Make sure you add this to the includeFields for the header and not one of the details.
Whenever you change one of the SData definition files, you need to restart Tomcat to have it take effect. After you do this you should see the new field on the form and be able to set and edit it.
After
Here is the screen shot with these two customizations:
Summary
Hopefully this starts to give some idea of how to customize the new screens. If you have the SDK then you can use the SwtUIDesigner to edit these forms; but, hopefully in future versions the visual screen designer will be included in the main product making this process much easier for non-SDK partners to customize.
But at least for now, once you get used to XML you can do a lot of customizations to the current Web based version 6.0A screens.
Writing Server Side Code for Accpac 6 Web UIs
We discussed how to create Accpac 6 Web based UIs in http://smist08.wordpress.com/2010/08/14/creating-a-web-form-for-accpac-6/ and how to write code that runs as JavaScript in the Browser in http://smist08.wordpress.com/2010/08/21/client-logic-for-an-accpac-6-web-form/. This blog posting looks at how you can write server side code in Java to assist your UIs do their job more efficiently.
Accpac 6 Web UIs communicate with the Server using SData (http://smist08.wordpress.com/2009/11/24/sdata-in-sage-erp-accpac-6/ and http://sdata.sage.com/) which is a REST based Web Services protocol. On the Server we run a Java application that converts incoming SData requests into Accpac View calls. Accpac Views are the business logic objects within Accpac. The Accpac business logic is remaining largely unchanged in this new environment and it’s the job of this Java application to convert the new world into the older world.
Theoretically then, you could do much of what we are talking about directly in the Views. We could expose new calculated fields or new functionality directly from the Views. However Views are used by all sorts of things including VB based UIs, macros, import/export as well as by third party applications. We don’t want to clutter up the Views with lots of extra logic needed to support or optimize the new Web based UIs. What we are looking to do is split the code that used to run entirely in VB UIs to half run in the browser and half run on the server. We also want to provide support for our Web based UIs to minimize program size running in the Browser and to minimize the number of RPC (remote procedure calls) they need to make to the server. The current VB UIs call many Views at once to do what they need, but this won’t work so well over the Internet since it will require many SData calls to do things, we really want to consolidate all the info the UI requires in one feed, so it only needs to do one SData call to get everything it needs.
Within the SData server’s configuration are a number of XML files that define all the SData feeds for the various accounting applications. For each application there is a classmap.xml file that lists all the feeds and the Java class that processes that feed. Then for each feed there is a specific resourcemap.xml file that defines various properties or configurations for that feed such as which Views is sits on, or some extra fields to present. To add an SData feed on top of an existing View is easy and just a matter of creating these simple XML files. There is a generic class ViewResourceKind that will provide standard SData functionality for any View. But if you want to add your own functionality you need to extend one of our classes and add your own logic.
The hardest way to implement an SData service is to create a class that implements our SDataResourceKind interface. This interface defines what a class needs to implement in order to be used as an SData feed from our SData application server. Here you need to do all the work, but if you want to implement a feed that hasn’t got anything to do with normal Accpac processing, perhaps this is a way to go. Fortunately we provide a class that implements this interface that you can extend. This is the ViewResourceKind class, mentioned previously, that implements SData on top of a View. This is really intended for data type Views. You can extend this to add fields and/or provide other helpful services for your UI form (or other consumer of your SData feed).
SData provides data type feeds similar to Views built closely over database tables, but it also provides “service” feeds that are similar to Accpac superviews. These feeds are meant to perform service type operations like printing a report, posting a batch or doing a calculation. They can operate asynchronously, meaning the originating call returns immediately, but then can poll the service for status updates (which can be used to update a meter control).
Here is a bit of sample code from the SData service that provides the data for the pie charts in the G/L Balance Sheet data snapshot.
public class GLBALSHTService extends BaseService
{
private final SDataView viewGLFSUM;
private final List<ServiceField> requestFields;
private final List<ServiceField> responseFields;
private static final String STR_RSCID = "GLBALSHTSP";
private boolean bPermitted = false;
private GLLanguageResourceContents GLLanguageResource;
public GLBALSHTService(final ApplicationContext applicationContext, final ResourceContextImpl resourceContext,
final Resource resource, final Service service, final SDataViewSet viewSet)
{
super(applicationContext, resourceContext, resource, service, viewSet);
viewGLFSUM = new SDataView(getResourceContext().getAccpacProgram(), GLFSUM.VIEW, resource);
bPermitted = getResourceContext().getAccpacProgram().isPermitted(STR_RSCID);
if (!bPermitted)
{
String appVersion;
String appLang;
//language dependent resource content is only used when access is not permitted. appVersion = resourceContext.getAccpacProgram().getActiveApplications().get(GLLanguageResourceContents.APPL) .getAppVersion(); appLang = viewGLFSUM.getProgram().getSession().getUserLanguage(); GLLanguageResource = new GLLanguageResourceContents(appVersion, appLang); }
requestFields = new ArrayList<ServiceField>(); responseFields = new ArrayList<ServiceField>(); setupRequestFields(); setupResponseFields();
}
@Override
public void shutdown()
{
if (this.viewGLFSUM != null)
this.viewGLFSUM.dispose();
}
private void setupRequestFields()
{
requestFields.add(ServiceHelper.viewFieldToServiceField(viewGLFSUM.getFields().get(GLFSUM.IDX_YEAR)));
requestFields.add(ServiceHelper.viewFieldToServiceField(viewGLFSUM.getFields().get(GLFSUM.IDX_PERIOD)));
requestFields.add(ServiceHelper.viewFieldToServiceField(viewGLFSUM.getFields().get(GLFSUM.IDX_PERIODS)));
}
private void setupResponseFields()
{
responseFields.add(ServiceHelper.viewFieldToServiceField(viewGLFSUM.getFields().get(GLFSUM.IDX_ENDDATE)));
responseFields.add(ServiceHelper.viewFieldToServiceField(viewGLFSUM.getFields().get(GLFSUM.IDX_CASH)));
// … Lots more response fields defined …
}
protected List<ServiceField> createRequestFields(final SDataRequest request)
{
return requestFields;
}
@Override
protected List<ServiceField> createResponseFields(final SDataRequest request)
{
return responseFields;
}
@Override
protected List<ServiceField> execute(SDataResourceElement payload, SDataRequest request, AsyncStatusListener listener)
{
List<ServiceField> list = new ArrayList<ServiceField>();
if (!bPermitted)
{
throw (new RuntimeException(GLLanguageResource.getValue(GlobalContent.ACCESS_DENIED_MSG.toString()), null));
}
int errorCode;
this.viewGLFSUM.recordClear();
setGLFSUMViewFieldsValue(payload);
MeterEventListener meterListener = null;
if (listener != null)
{
meterListener = new MeterListener(listener, listener.getProgress().getProgressPct(),
TrackingPayload.PROGRESSPCT_GET_RESULT);
}
errorCode = viewGLFSUM.process(meterListener);
if (errorCode > 0)
{
throw (new RuntimeException(null, null));
}
list.add(ServiceHelper.viewFieldToServiceField(viewGLFSUM.getFields().get(GLFSUM.IDX_ENDDATE)));
list.add(ServiceHelper.viewFieldToServiceField(viewGLFSUM.getFields().get(GLFSUM.IDX_CASH)));
// … Lots more reponse fields added …
return list;
}
/**
* function to set field value
*/
private void setGLFSUMViewFieldsValue(SDataResourceElement payload)
{
for (SDataResourceElement elem : payload.getContents())
{
final String name = elem.getName();
String value = (String)elem.getValue();
if (value != null)
{
viewGLFSUM.putBySDataPropertyName(name, value, Boolean.FALSE);
}
}
}
/**
* set the default value for the request fields
*/
@Override
protected List<ServiceField> createTemplateFields(final SDataRequest request)
{
List<ServiceField> list = new ArrayList<ServiceField>();
this.viewGLFSUM.recordClear();
list.add(ServiceHelper.viewFieldToServiceField(viewGLFSUM.getFields().get(GLFSUM.IDX_YEAR))); list.add(ServiceHelper.viewFieldToServiceField(viewGLFSUM.getFields().get(GLFSUM.IDX_PERIOD))); return list; } }
This service is then defined in the GLServiceMap.xml file under the tomcat\portal directory:
<?xml version="1.0" encoding="UTF-8"?> <serviceMap> <services> <service className="com.sage.accpac.gl60a.common.server.GLBALSHTService"> </service> … other definitions … </services> </serviceMap>
In the execute method, notice the View calls to the GLSUM View. You can see calls to recordClear and process. Basically these are calls to our Java JNI layer (Java Native Interface, http://en.wikipedia.org/wiki/Java_Native_Interface). Here we have a hierarchy of classes very similar to what we had in COM for VB programming where you can access all the View methods as well as a selection of convenient other System Manager APIs.
The basic logic here is to first initialize the operation in the constructor, get the input fields from the SData feed (the request fields as in setupRequestFields), perform the various View operations that are required (the execute method) and then setup the response fields to send back to the UI running in the Browser (created in setupResponseFields and then set in the execute method).
Hopefully, this gives a bit of a flavor for what sort of programming goes on the server for helping User Interface programs from an SData point of view.
Client Logic for an Accpac 6 Web Form
Last week we talked about creating Accpac 6 Web UIs (http://smist08.wordpress.com/2010/08/14/creating-a-web-form-for-accpac-6/). Part of the process is creating the XML representation of the Web Form which we discussed previously: http://smist08.wordpress.com/2010/05/28/how-to-layout-forms-in-sage-erp-accpac-6/. Now suppose you have an XML form designed either in an XML editor or using our SwtUIDesigner and you want functionality beyond that which is given to you by default. By default you have record navigation, save and delete. From last week’s article you will now have a simple runnable project. So where do you put your client side code? Within the client files is a file xxFeatureModule.java which contains:
/* * Copyright 2010 Sage Software Inc. All rights reserverd. */
package com.sage.accpac.SS60A.ss1005.client;
import com.sage.swt.client.ui.builder.AbstractSynchronousFeatureModule; import com.sage.swt.client.ui.builder.InstanceContext;
public class SS1005FeatureModule extends AbstractSynchronousFeatureModule
{
public String getDebugName()
{
return "SS1005 Feature Module";
}
protected boolean handleInstanceLoaded(InstanceContext context)
{
// TODO: Handle what happens when an instance context is loaded.
// Return true if the handling was successful; false otherwise.
return true;
}
}
This is where you put your code. The handleInstanceLoaded routine is provided for you to hook your code in. Basically this is called once the UI has initialized and the XML form has been processed and everything has been created. At this point you can hook into any UI controls, data sources or do any other initialization you require. The context that is passed into this routine is the object from which you can get to everything else in the UI. It contains the collection of controls on the form, collection of datasources , etc. Below is a snippet of code for a simple handleInstanceLoaded routine that retrieves a button from the context by calling its getWidgets() method. Then it calls addClickListener() to add a listener that will be called whenever the button is clicked.
protected boolean handleInstanceLoaded(InstanceContext context)
{
DefaultAllClickListener defaultAllListener = new DefaultAllClickListener(context);
SwtButton btnDefaultAll = (SwtButton) context.getWidgets().get
( UIConstants.WidgetIDs.CONFIG_BTN_DEFAULTALL );
if (null != btnDefaultAll)
btnDefaultAll.addClickListener(defaultAllListener);
return true; }
private class DefaultAllClickListener implements ClickListener
{
public DefaultAllClickListener(InstanceContext context)
{
}
public void onClick(Widget sender)
{
SwtMessageDialog.showMessageDialog(
UILanguageResources.AppContent.DIALOG_RESTORE_DEFAULT.getValue(),
UILanguageResources.AppContent.DIALOG_CONFIRM_DEFAULT.getValue(),
SwtMessageDialog.ResponseButtonSetType.YES_NO,
SwtMessageDialog.MessageType.CONFIRM, "",
new MessageDialogListener[] {new ConfirmDefaultAllListener()});
}
}
private class ConfirmDefaultAllListener implements MessageDialogListener
{
public void onRespond(MessageDialogEvent evt)
{
SwtMessageDialog.ResponseButtonType response = evt.getResponse();
if (response == SwtMessageDialog.ResponseButtonType.YES)
{
defaultConfigInfo(popupContext);
}
}
}
Notice that we defined a new class DefaultAllClickListener which implements ClickListener. Basically this is defining the class for the object that will be called whenever a button is pressed. So in handleInstanceLoaded we call “new DefaultAllClickListener(context)” to create a new object in the DefaultAllClickListener class. This object will now be called whenever the button is pressed. In Java this is the typically way you get event notifications. There is nothing like a COM Event special type of method. Event handlers are just regular methods in regular classes. You hook them up by passing them to the object that does the notifying, usually by some sort of addXXXListener() type method call. It’s up to each routine that does notifications to keep a list of all the objects they need to notify and to call each one in turn whenever anything happens.
This is true for all UI events, notice that even the confirmation dialog displayed has a ConfirmDefaultAllListener() that will be called when the button is pressed on this dialog. All notifications are asynchronous like this. You can’t call a UI action and expect to wait (in the code) for a response. If VB we could just do answer = MsgBox () and our code would wait for the user to choose Yes or No. In the browser, everything is asynchronous, nothing waits, everything is via notifications. This is a different metaphor than people are used to. In languages like VB some things are asynchronous and work via event notifications and some things are synchronous. The big difference is that calls to the server are asynchronous, so whenever you make an SData call, you are notified via this mechanism when something arrives. Contrast this to VB where you make a server call, your code waits for the response and no more code is executed until you get it. In the web world this could take quite a long time and the browser would be completely unresponsive if no code executed until you got this response. This is the world of AJAX (Asynchronous JavaScript and XML http://en.wikipedia.org/wiki/Ajax_(programming) ). AJAX is the key to creating rich internet applications in the Browser. If we didn’t use this, then the input forms would be quite boring lists of titles and edit boxes followed by a submit button, where nothing happens until you hit submit. This is another reason that you want to put groups of View calls in your server module, where everything works synchronously as normal. Generally you only want to make one server call at a time, since then you don’t need to chain together a number of responses, plus you get the performance benefit of only making one server call over the Internet.
Here we’ve been putting the code in the xxFeatureModule.java file, but unlike VB, there are no restrictions on how many source files you have and no rules that certain things have to be in certain files. So you can freely create as many class files as you like and organize your code in whatever manner you like. The nice thing is that you can follow industry best practices and aren’t under constraints like an event listener has to be in a special file in order to be called. So the expectation would be that you can create common Java libraries to share over UI projects and that you can keep your class files small and maintainable without putting everything including the kitchen sink into the xxFeatureModule.java file.
One aspect of Java that trips up VB (and C) programmers is the use of inheritance. When using an API or framework in C or VB, basically you look for all the functions, properties and events you can use. If you take this approach with Java, you will probably find that you are having trouble finding what you are looking for. This is because Java supports inheritance in a proper object oriented manner. So often the paradigm isn’t to do something by calling API functions, rather what you do is extend a class that does much of what you want and then just add the code that’s needed to make the difference. People new to Java often take a bit of time getting used to this. If you are stuck and aren’t finding an API that you are sure must be provided, instead look for a class that is along the lines of what you need that you can extend.
This code is Java and definitely not JavaScript. Yet this is what we are writing to run in the Browser. To produce the final runnable code we have to feed this through the GWT Compiler to get it compiled into JavaScript. The GWT Compiler will produce 6 versions of JavaScript from this code, one optimized for each major Browser family including WebKit (Safari and Chrome), Gekko (Firefox) and separate versions for IE 6 and 8. This then is the final code that you would deploy to your customer’s web servers to run.
In the meantime while you are developing, you keep working in Java. You can run this code as a Java application inside the Eclipse IDE. You can use the Eclipse Java debugger to single step through the code, set breakpoints and examine variables. You can also use all sorts of other Java tools to make yourself more productive like JUnit (http://www.junit.org/), Emma (http://emma.sourceforge.net/) or TPTP (http://www.eclipse.org/articles/Article-TPTP-Profiling-Tool/tptpProfilingArticle.html). Plus you can use all the plug-ins that are available for Eclipse such as say SubVersion for source control (http://en.wikipedia.org/wiki/Apache_Subversion).
All the methods are documented in the HTML JavaDoc which is generated from the comments in the source code. This is included as part of the SDK. Below is a sample of the beginning of the page that documents the InstanceContext. This page documents all the methods in this object.
Customization
Generally you can do quite a bit of customization just with the screen XML file. But at some point you are going to have to write some code to do the more deep customizations. To customize such UIs with code, you basically want to get hold of the context object, so you can access the collections of widgets and datasources. Then you can add your own listeners, or change the properties of the various objects. Basically you extend the main entry point class of the UI and extend the xxFeatureModule class. Add your functionality and then feed everything back through the GWT compiler to generate a new set of web pages to deploy.
Summary
Hopefully this gives a flavor for how the Browser part of a UI control is coded in the new Accpac SWT framework. Most of the basic functionality is automatic from the XML screen definition, but UIs are more complex than just CRUD (Create/Read/Update/Delete) and this is how you add that extra complexity to handle sophisticated UI needs.
Creating a Web Form for Accpac 6
Sage ERP Accpac 6.0A is just entering beta (lucky Friday 13th August, 2010). With this release you will be able start to run Web based User Interface forms. We’ve covered how this looks in several other blog posts like http://smist08.wordpress.com/2009/12/03/the-sage-erp-accpac-6-0a-portal/ and http://smist08.wordpress.com/2009/12/24/sage-erp-accpac-6-0-data-portlets/. But as a developer, how do you create new Web Based forms using the Accpac SDK?
The new Web based forms are written using a number of tools that are integrated into the Eclipse development environment (http://www.eclipse.org/). Our SDK involves programming with the Sage Web Toolkit (SWT) which is based on the Google Web Toolkit (GWT – http://code.google.com/webtoolkit/). With this toolkit you develop your programs in Java and then GWT provides a compiler that will compile these programs into JavaScript, which will run in your browser.
To start with we provide a project wizard integrated into Eclipse. You use this to create a starting Accpac screen project. From the File menu in Eclipse you choose File – New – Project… Then select under “Sage Web Toolkit”, “New SWT UI”. This gives the following dialog that gets the basic information on the UI you want to create:
When you are done and click Finish, our wizard will create a new project in Eclipse. This eclipse has a simple UI form and everything you need to start working on your UI:
This is a completely ready to run minimal UI that is all set for you to add your own logic to. If you don’t do anything and just choose run, then your project will be run in GWT hosted mode, which means it is run as a Java program in a container that simulate browser behavior. This is how you debug your programs. In this mode you can set breakpoints, single step through code, examine variables and do anything else supported by the full Java debugger. When you run it will pull up the sign-on screen:
And then when you sign-on you get the screen with the single table control connected to the A/R customers SData feed that we put on the form. Normally you would delete this control and then add the controls you really want on your form.
Behind the scenes is the development shell that show useful messages on what is going on, especially if something goes wrong. Plus status messages are sent to the Java Console inside the Eclipse IDE.
Once you have debugged the program and have it working then you run the project through the GWT compiler to produce a Web HTML/JavaScript version of the program that you can run directly in the Browser, or which you can add to the Accpac Portal.
When you are writing code for your UI, it can go into one of two places. If can reside on the Browser side of things running as JavaScript or it can run on the server side as an SData service (http://sdata.sage.com/ or http://smist08.wordpress.com/2009/11/24/sdata-in-sage-erp-accpac-6/). In VB all the UI code was within the VB OCX control and this talked directly to the Accpac Views (Business Logic Objects). In the Web world we have JavaScript running in the Browser (created by compiling Java programs with GWT), that talk to SData services running on the Server. These SData services then talk to the Accpac Views.
When programming the Browser half, you have nearly all the power you had in VB. You can add listeners to any control and get events as things happen. You can add listeners to data sources to get notified as things happen to data. This is very similar to VB event handlers. You can make SData calls that translate directly into View calls, like you called the Accpac COM API in VB. However there are some major differences. Just because you can do something, doesn’t mean it’s a good idea. In the Web world you want as much to happen on the server as possible. Any code you add to the JavaScript side, increases the size of JavaScript that has to be downloaded to the Browser (it will be cached and JavaScript code is very compact). Plus whenever you make an SData call to the Views, this call will happen over the Internet. This means it could be slow. Generally you only want to make 1 SData call to the server to do anything. You don’t want to make a bunch of SData calls to accomplish something. You really only want to make one call. Additionally in the Web world all calls are asynchronous, meaning that other processing continues after you send off the request. When you send the call you set a listener, which will be called when the response comes back from the server. This makes it quite complicated to make multiple calls and we don’t provide any help to do this in our framework (and neither does GWT), because we really want to discourage it.
So how do you do complicated sets of View operations? You create these on the server where we’ve made it easy. We let you create your own SData services or extend the standard services that we put on top of all the Views. So for instance to perform the operation to calculate taxes, you would send all the input fields in a service SData request, the server Java class would do all the viewPuts, viewProcess, etc and return any fields that changed as a result. This way you make 1 SData call from the UI, the SData process on the server makes all the View calls and returns the request. This minimized network time both in latency and bandwidth. On the server we have a generic class that wraps a View, this class is responsible for converting SData requests into View calls. It handles all the standard View protocols. You can extend this class with your own class and add extra logic to it. You can easily add additional fields from other Views or calculated fields. Generally you would like to only interact with one SData source from a UI, so you want to include any side lookups like description fields into the main SData source. You also would rather do calculations on the server for performance reasons, plus then anyone else using your SData service benefits from this code. All these server classes are controlled by a number of XML configuration files that define all the SData sources and configure which server side Java classes process them.
This was a quick overview of how to create a new Accpac Web based UI along with a bit of discussion on conceptually what you do next without any details. Hopefully in future blog posts we can fill in a few more details on how to program both the Browser and server in this new model.



















