Tuesday, September 30, 2014

How to implement a birthday reminder - Pt. 1

The Contacts (relationship) table in X3 (function GESAIN) provides a Birth date field. We can use this field to create an automatic birthday reminder that is going to popup on login with the names of our business contacts that have a birthday.

First we are going to create the popup window. We go to the Screens function (GESAMK) and create this screen:

General tab:
 Fields tab:

We validate the screen and exit. Then we go to the Windows function (GESAWI) and create a window for our screen:


In the Predefined buttons tab we can set to No all predefined buttons except for the OK button, which should be set to Yes.

We validate the window and exit. Now we go to the Actions function (GESACT) and create an action that is going to open the window:


Here we run into a problem. We are going to use the workflow engine of X3 to show the popup on login. But an action that displays a window cannot be used in a workflow (as you can see above, the Workflow checkbox is grayed out). To solve this, we are going to create another action that will call the above action, and use this new action in the workflow.

So, we create one more action:

This time the template is Outside model, so we have the Workflow checkbox available. Make sure it is checked.

In the second part of this tutorial we are going to do the custom programming and setup the workflow.

Monday, September 29, 2014

How to load the value of a custom screen field

Sage X3 provides unlimited possibilities for screen customization - fields can be added to any screen and grid. But if we add a field that is not part of the main object table, we might need a bit of custom programming in order to load its value when the screen loads. Here is an example of how to do this (based on a forum question)

Suppose that in the Calls function (GESCLL) we want to add a field that will show the position of the selected contact in his organization. Like this:










The field we need is CNTFNC in the Contacts table. First we create the corresponding screen field in the Screens function (GESAMK), screen CLL0:


After we validate the screen, from the General tab we access the SPECLL file, where we enter the following code:

$ACTION
Case ACTION
    When "OUVRE" : Gosub OUVRE
    When "LIENS" : Gosub LIENS
    When Default
Endcase
Return

$OUVRE
If clalev([CNT])=0 : Local File CONTACT [CNT] : Endif
Return

$LIENS
Read [CNT]CNT0=1;[M:CLL0]CLLCMP;[M:CLL0]CLLCCN
If !fstat
    [M:CLL0]CNTFNC=[F:CNT]CNTFNC
Endif
Return

In the opening action OUVRE we make sure to open the CONTACT table, if it is not already opened. Then, in the action LIENS which is called on each screen update but before the screen is displayed, we read in the contact and, if there are no errors, assign his function in the organization (CNTFNC field) to our custom field of the same name.

You might need to logout/login before this custom development becomes active. Also, do not forget to save and compile the SPECLL file, as well as to validate the CLL0 screen.

How to perform an action on field update

Here is an example of how to perform an action after a field is updated. This is a general technique that can be used on any field.

Let's look at the Products function (GESITM), Units of measure tab:


We have a lot of different units and that gives us flexibility, but suppose that in our case all products will always have the same Stock unit, Purchase unit, Sales unit, Statistic unit and EU unit. Entering all those units manually becomes unnecessary and error-prone. What we want is to only enter the Stock unit and have the other four units change automatically.

First of all, we will set the four secondary unit fields to an Input type of Display, so that they cannot be modified directly. We do this in the Screens function (GESAMK), where we navigate to the ITM3 screen and change the appropriate values from Enter to Display:


Now, in the same screen, we go to the STU field and assign to it an After Change action with a value of SPE:


SPE means that the action handler is going to be located in the specific processing file for this screen. We validate the screen and go to the General tab, from where we can access the specific processing file SPEITM. There we need to type in the following action handler (replacing the default AM_STU subprogram, if such already exists in the file)

Subprog AM_STU(VALEUR)
Variable Char VALEUR()

[M:ITM3]PUU=VALEUR
[M:ITM3]SAU=VALEUR
[M:ITM3]SSU=VALEUR
[M:ITM3]EEU=VALEUR

Affzo [M:ITM3]PUU
Affzo [M:ITM3]SAU
Affzo [M:ITM3]SSU
Affzo [M:ITM3]EEU
End

This handler will be called each time the value of the STU field is modified. The new value is passed in the VALEUR parameter. So we assign this value to the other four unit fields and then we use the Affzo instruction to update their screen presentation. This way we will always change only the Stock unit (STU field) and the other four unit fields will be synced automatically.

Sunday, September 28, 2014

Watch out for your batch license

The function VISULIC shows you your current X3 license. There is a line in that screen that shows the number of possible simultaneously executing batch processes:


In the above case, it says 5. But the real number is less than that. You have one slot always occupied by the batch server itself and one more by the accounting tasks - that leaves you with 3.

If you are planning to have a lot of custom developments in your X3 solution, you want to ask how many simultaneously executing batch processes your license will allow. If the number is too low, it might prove to be a limiting factor at some point.

Saturday, September 27, 2014

How to implement a right-click tunnel

Sage X3 is chock-full of right-click options, they are basically everywhere. So here is how we can implement our own.
We are going to create a right-click tunnel to the Products function (GESITM) in the grid lines of the Delivery function (GESSDH). Such a tunnel already exists on the field [M:SDH1]ITMDES:


However, there are some problems with that. First of all, it is two clicks too many - we have to double-click the Description field first, and then right-click. Secondly, if we are on the other end of the line, we have to go back all the way to the left side. And lastly, if the delivery is posted, we cannot even enter the Description field. So if we find ourselves tunneling to the Products function often enough, we might want to implement this functionality generally on the line, and not on a specific field.

We will do this by going to the Screens function (GESAMK), navigating to the Delivery details screen (SDH1) and defining a Button action on the bottom of grid variable NBLIG:
































In my case, the first free Button action is Button 17, so I use that one. I assign to it the action GOBJETC2, which opens a new object (GOBJETC0 and GOBJETC1 are equivalent actions, but in my case they are already used). My action has three parameters in the right panel:
OBJCLEC2 - the code of the object that will be opened. We assign the product code field ITMREF.
OBJETC2 - the abbreviation of the object to be opened. In our case, "ITM" for Products.
OBJZONC2 - not used.
In the left panel, we also assign a default title of "Products".

This is enough for the functionality to work, but we can put one more finishing touch. When we right-click, we don't want the tunnel label to be the default "Products", but "Product XXX", where XXX is the product to be opened. To achieve this, we must define one more action on the NBLIG variable:
































The action is of type Init Button, and we assign SPE to it. SPE means that the action handler will be located in the specific processing file for this screen - namely, SPESDH. After we validate the screen, we can go to the General tab and open the SPESDH file. We want to type in the following handler for the Init Button action:

Subprog IB_NBLIG
Raz GBOUT17
If [M:SDH1]ITMREF(nolign-1)<>""
    GBOUT17="Product"-[M:SDH1]ITMREF(nolign-1)
Endif
End

GBOUT17 is the global variable that holds the title of our Button 17. First we clear it, and then, if we have a product code on the current line, we assign to GBOUT17 the string "Product XXX", where XXX is the product code - exactly what we wanted. We save and compile, and we can close the SPESDH file.

The Delivery function is transaction managed, so the last thing we have to do is validate the Delivery transactions (function GESSLD). After we do that, we can use our new functionality in GESSDH:


This is a general technique that can be used in any X3 grid to open all kinds of objects and documents.

Friday, September 26, 2014

How to use the import templates programmatically

Sage ERP X3 provides a great number of import templates (function GESAOE) that can be used when performing data migration from another application. But another option is to use the import templates programmatically in order to create documents and records on the fly. Here is how.

Local Char WNOMFIC(250)
WNOMFIC="C:\SAGE\SAGEX3V6\X3V6\Folders\DEMO\ZIMPEXP\IMPOR\SMR"

Openo WNOMFIC, 0 Using [ZSMR]

Iomode adxifs "" Using [ZSMR]
Iomode adxirs chr$(13)+chr$(10) Using [ZSMR]
Iomode adxium 50 Using [ZSMR]

Wrseq "E;19;;210;20140911;Misc. receipt;;" Using [ZSMR]
Wrseq "L;1000;BP20;My item;UN;10;UN;10;10" Using [ZSMR]
Wrseq "S;UN;10;;;;C110;A;" Using [ZSMR]

Openo Using [ZSMR]

Call IMPORTSIL("SMR", WNOMFIC) From GIMPOBJ

In the above example, we first create a local char variable WNOMFIC and assign to it the path where we are going to save the temporary file that will be used by the import template (C:\SAGE\SAGEX3V6\X3V6\Folders\DEMO\ZIMPEXP\IMPOR\), as well as the file name (SMR). Both can be different from what is shown in the example. Also, the file could have an extension (SMR.dat, SMR.txt, etc.), but this is not mandatory.
Then we write to the file using standard 4GL instructions (Openo, Iomode, Wrseq). What we write is the actual content of the import template, as specified in the GESAOE function. In this case it is the SMR template for miscellaneous receipts.
Finally, we run the import template with the IMPORTSIL function. This function is located in the GIMPOBJ file and takes two parameters - the import template code and the name of the file to be used by the import template.

With this technique we can create all kinds of documents and records programmatically - orders, invoices, products, customers, accounting entries, etc. And, of course, the data can be dynamic. For instance, in the above case we could have a screen where the user selects product, quantity, storage site and location. The values of the corresponding fields in the import template file would then be supplied by the user entries (for example, the first string written to the above file could be

Wrseq "E;19;;"+[M:ZES]FCY+";20140911;Misc. receipt;;" Using [ZSMR]

supposing [M:ZES]FCY is the screen field where the user enters the site code). This would give us a neat and quick functionality for adding stock to our storage sites.

Thursday, September 25, 2014

How to quick search without the pesky stars

When you quick search in a left list or a selection box in Sage ERP X3 V6, you have to enter STAR-SEARCH STRING-STAR each time (for example: *MyCompany* to find all occurrences of MyCompany), otherwise the search won't work and you will get no results. This is quite annoying, so here is how you can make the application add the stars automatically.

We are going to use a couple of entry points, one of which is located in the GACTION file, and the other in GOBJACT. So you must have the following setup in your entry points function (GESAPE):


ZSELRAP is the name of the file where we are going to write the custom code for the entry points. Of course, you can give it a different name. In this file, you have to place the following code:

$ACTION
Case ACTION
    When "SELRAP"    : Gosub SELRAP
    When "SELRAPOBJ" : Gosub SELRAPOBJ
Endcase
Return

$SELRAP
For I=1 To NBCOL
    If SAIRAP(I)<>"" & !instr(1, SAIRAP(I), "*")
        SAIRAP(I)="*" + func AFNC.TRANSF(SAIRAP(I), " ", "*") + "*"
    Endif
Next
Return

$SELRAPOBJ
For I=1 To NBSEL
    If SAIRAP(I)<>"" & !instr(1, SAIRAP(I), "*")
        SAIRAP(I)="*" + func AFNC.TRANSF(SAIRAP(I), " ", "*") + "*"
    Endif
Next
Return


Two entry points are used: SELRAP and SELRAPOBJ. One of them takes care of the quick search string in the left box, and the other - in the selection box. But the functionality is exactly the same: we loop through the quick search strings for all columns (available in the indexed variable SAIRAP) and if the quick search string is not empty and does not already contain stars (meaning that we explicitly want to use the star functionality), stars are added to the beginning and the end of the search string, as well as in the middle, replacing all spaces.

To make the newly created entry points functional, you might need to logout of X3 and login again.

So now, if we enter the search string MyCompany, it is going to be transformed to *MyCompany* automatically. And My Company will become *My*Company*. This way you can stop entering manually all those stars every time you want to quick search in a left list or a selection box.