Wednesday, December 3, 2014

How to change the Enter key functionality

In Sage X3 screens, the Enter key usually has some default functionality - for example, it might be triggering the OK button. Regardless of what the actual default functionality is, here is how you can block it or replace it with something else.

When the Enter key is pressed, X3 assigns the value GSTARET to the variable REPONSE. You can check for that condition in the 'After Choice' action. Depending on the template you are using, this action might be APRES_CHOI or AP_CHOIX. For the examples below, we will use APRES_CHOI.

First of all, you need to add the action to the list of actions in the $ACTION label of your specific processing file like this:

$ACTION
Case ACTION
# ... Other actions ...
  When "APRES_CHOI" : Gosub APRES_CHOI
# ... Other actions ...
  When Default
Endcase
Return


Then, in the action handler, if you just want to stop the standard Enter key functionality from executing, you can do:

$APRES_CHOI
If REPONSE=GSTARET : REPONSE=0 : Endif
Return


And if you want to replace it with new functionality:

$APRES_CHOI
If REPONSE=GSTARET
    REPONSE=0
    Infbox("Custom functionality goes here!")
Endif
Return


Once you make sure that the Infbox statement is getting executed when you press Enter in your screen, you can put in its place something actually useful, depending on your needs.

Thursday, November 13, 2014

How to tab directly to a non-neighbour field in a grid

Tabbing through default values can get boring if you have to do it a lot. The good news is, with Sage X3 you don't have to do it at all!

Suppose, for example, that you enter Sales orders in the following screen (GESSOH function)


On each row, you enter the product code and then you tab through to the Ordered qty field, accepting the default values for Description, Ship site and Sales unit. This is three tabs too many. Wouldn't it be great if you could just hit Tab once in the Product field and get directly to the Ordered qty field, with the default values appearing as they normally should in the three fields in-between?

Well, here's how to do just that.

You need to create an After change action on the Product field. For the Sales orders, this is the [M:SOH4]ITMREF field. You must open the SOH4 screen in the GESAMK function and create the appropriate action like this:


In your specific processing file SPESOH, in the After change action handler (AM_ITMREF) you must assign a value to the zonsui variable, which contains the next field to be entered. To go to the Ordered qty field on the current line, this value must be QTY(nolign-1). So the AM_ITMREF handler should look like this:

Subprog AM_ITMREF(VALEUR)
Variable Char VALEUR()
zonsui="QTY(" + num$(nolign-1) + ")"
End

After you validate your screen and your entry transactions, you will have the desired effect of tabbing directly from Product to Ordered qty field when you enter Sales orders. And you can use this technique in any other screen you want.

Tuesday, November 4, 2014

How to prevent the printing of a report

To prevent the printing of a report, you can create a specific processing for the report and put the following code in the file:

Subprog IMPRIME(NBPAR, PARAMETRE)
Value Integer NBPAR
Variable Char PARAMETRE()()
If ... ... ... ...
    GOK=0
Endif
End


The principle is that when you set GOK to 0, the report will not be printed. Of course, you have to substitute the dots after 'If' with the appropriate condition. For example, suppose that you print a report from the Deliveries function (GESSDH) and you want to print it only if the delivery is validated. To prevent the printing otherwise, you might have:

Subprog IMPRIME(NBPAR, PARAMETRE)
Value Integer NBPAR
Variable Char PARAMETRE()()
If [M:SDH1]CFMFLG<>2
    GOK=0
Endif
End

Saturday, October 25, 2014

How to activate a custom menu item

In the GESAWI function, Buttons/menus tab, you can add custom menu items to any Sage X3 window like this:


After you validate the window (and the entry transactions, if it is an object managed with entry transactions) you will see your custom menu item when you open the window, but it will always be grayed out. That is the default. To activate it, you need a bit of custom programming.

In the specific processing file for your window you must handle the SETBOUT action and put the following code in the action handler:

$SETBOUT
CHMEN += "O"
Gosub SET_BOUT_SPE From GSAISIE
Return

The principle is this: You add the codes of the menu items that you want to activate to the CHMEN variable and then call the SET_BOUT_SPE standard function. You use only the second letter of the code - in the above screenshot you can see that the actual code of our custom menu item is DO, but we only add O to CHMEN.

You can add more than one code at once. For example, if you have a custom menu item with code DO (as shown above) and another one with code DP, you can add them by saying

CHMEN += "OP"


And, of course, you can have an If statement and only activate a menu item when certain conditions hold.

When adding menu item codes to the CHMEN variable, be careful to use += and not =.

Tuesday, October 21, 2014

How to lock out a user on unsuccessful login

To boost the security of Sage X3 and prevent brute-force attacks, you might want to lock out a user after three unsuccessful login attempts. Here is how to do it, using workflow and a helper table:

First we will create the helper table. We do this in the GESATB function:

Fields tab:

Index tab:


This table will hold the number of unsuccessful login attempts for each user.

Next we need an action that will be triggered by the workflow. We create it in the GESACT function:


Do not forget to mark the Workflow checkbox!

Now we go to the Workflow rules function (GESAWA) and create the following workflow:

General tab:

Recipients tab:
Only one row. All fields should be empty or have the value of "No", except for Type and Recipients.

Action tab:

And the last thing we need to do is create the standard processing file for our ZPASS action. So we go to the editor (function ADOTRT) and create the ZPASSSTD file with this code:

Subprog PASS()
If GERR=6
    If clalev([ZPS])=0 : Local File ZPASS [ZPS] : Endif

    Read [ZPS]ZPS0=GUSER
    If fstat=5
        Raz [ZPS]
        [F:ZPS]USER=GUSER
        [F:ZPS]COUNT=1

        Write [ZPS]
        If fstat
            Infbox("Error while writing [ZPS], fstat="+num$(fstat))
        Endif
    Elsif fstat
        Infbox("Error while reading [ZPS], fstat="+num$(fstat))
    Else
        If [F:ZPS]COUNT>=3
            If clalev([ZAUS])=0 : Local File AUTILIS [ZAUS] : Endif
            Update [ZAUS] Where USR=GUSER With ENAFLG=1

            Delete [ZPS] Where USER=GUSER       
        Else
            [F:ZPS]COUNT+=1

            Rewrite [ZPS]
            If fstat
                Infbox("Error while writing [ZPS], fstat="+num$(fstat))
            Endif
        Endif
    Endif
Elsif GERR=0
    If clalev([ZPS])=0 : Local File ZPASS [ZPS] : Endif
    Delete [ZPS] Where USER=GUSER       
Endif
End

And here is how it all works:

The workflow that we created is of type Miscellaneous and has the CON event code. This means that the workflow will be triggered on each connection to the application. The GERR global variable will be set to 6 if the provided connection password is incorrect (you might need to debug in order to see which is the right value of GERR - my Sage X3 documentation says that GERR is set to 1 when the password is incorrect, but in my case it turned out to be actually 6). The workflow runs the ZPASS action, which means that the code we placed in the ZPASSSTD file is executed. This code uses our ZPASS helper table to check if the user has three consecutive unsuccessful login attempts due to incorrect password, and if so, locks the user out by deactivating his record in the AUTILIS table (setting the ENAFLG field to 1).

Once locked out, the user cannot connect to X3 even with his correct password. An administrator will have to re-enable his record in the GESAUS function. And if the administrator locks himself out, he will have to change his record in the AUTILIS table (set the ENAFLG field to 2) directly in the database.

Tuesday, October 14, 2014

How to manually unlock a code file

When you open a code file in the Sage X3 internal editor (function ADOTRT), Sage X3 locks the file to prevent simultaneous modification. When you close the file, the lock is released. But if you experience an unexpected exit from the system while you have a file opened in the editor (for example, due to power failure), the file lock might not be released. In this case, when you open the file again, you will see a warning message and the file will be in read-only mode.

In such situations, to release the lock manually you have to delete a temporary file that X3 creates to mark the code file as locked. This temporary file has the same name as your code file and an extension of 'LCKsrc'. For example, if you open the SPEPOH code file, X3 will create a temporary file called 'SPEPOH.LCKsrc'.

The temporary file is created in the same directory as its corresponding code file. This is usually the TRT subdirectory of your X3 folder. To release the lock, all you have to do is delete the temporary file (be careful not to delete the .src and .adx files of the same name!)

Saturday, October 11, 2014

How to start a manual workflow programmatically

A manual workflow is usually started - you guessed it - manually, using the SAIWRKMAN function. But if you need to start a manual workflow programmatically, here is one way do it:

Local Char PARAM(GLONAWA)(1..3)
PARAM(1)="XXX"
PARAM(2)=""
PARAM(3)="1"

SAVGSERVEUR=GSERVEUR
GSERVEUR=2

Gosub INIT From SAIWRKMAN
Gosub CONTROLE From SAIWRKMAN
Gosub EXEC From SAIWRKMAN

GSERVEUR=SAVGSERVEUR

In the above code, XXX should be replaced by the name of the manual workflow that you want to run.

Keep in mind that depending on where you use it, this might change the values of some of your significant global variables and/or modify the masks and files by default. If such errors occur, you should program around them. For example, you can save the values of the affected global variables before running the above code and restore them afterwards.

Thursday, October 9, 2014

What are the GLON variables and how to use them

Every data type with alphanumeric internal type has an assotiated length. Let's take for example the Product reference data type ITM. Suppose it has a length of 20. So if you want to create a varibale that will take values of type ITM, you might write this:

Local Char WITMREF(20)

But like almost everything in X3, the length of an alphanumeric data type can be personalized. Also, it might change between versions of the software. If at some point in the future the length of the ITM data type changes to a value greater than 20, the above code will break.

So you might think of giving the variable a crazy upper limit:

Local Char WITMREF(200)

This will work for all practical purpoces, but it is a waste of resource, especially when you have a better alternative. This alternative comes in the form of the GLON variables.

The GLON variables are global variables maintained internally by X3. The name of each one starts with GLON and ends with the abbreviation of the corresponding alphanumeric data type. So for the ITM data type the corresponding GLON variable is GLONITM. The value of each GLON variable is the length of the corresponding data type. This value is updated on data type validation, so even if you customize your data type lengths or they change between versions, the GLON variable is guaranteed to hold the correct value.

And here is the proper way to define an ITM data type variable:

Local Char WITMREF(GLONITM)

Replacing ITM with another data type abbreviation, this will work for all other alphanumeric data types as well.

Saturday, October 4, 2014

How to impose custom password validation rules

If you want to create advanced password validation logic, Sage X3 has an entry point dedicated to this. To show how it works, we will create a rule that will reject a password not containing at least one digit.

The entry point we are going to use is called CTLPASSE and is located in the PASSE process. So first you need to go to your Entry points function (GESAPE) and declare the PASSE process like this:


ZCTLPASS is the name of the file that will contain the entry point code. Of course, you can give it another name. Here is the code for this file:

$ACTION
Case ACTION
    When "CTLPASSE" : Gosub CTLPASSE
Endcase
Return

$CTLPASSE
If !pat(PASSE, "*#*")
    Infbox("Error: The password must contain at least one digit.")
    GPE=1
Endif
Return

In the entry point, we are passed the new password in the PASSE variable. To reject the new password, we must set GPE to 1. In the above example, we do this when the password does not contain at least one digit. We check for this condition by pattern matching using the pat instruction. This is just a simple check, but the checks can be as elaborated as you want, so you can create any number of advanced password validation rules.

You might need to logout of X3 and login again before the entry point becomes active.

Friday, October 3, 2014

How to find the window a screen belongs to

In any opened X3 screen you can find the name of the screen by simply entering any of its fields and pressing F6. This pops up the field info, part of which is the screen name. But how about the window this screen belongs to? Probably there is some offical method to do this, but as I am not aware of it, here is a hacky but effective way to find the window name.

When you have your screen opened, in the top menu you select ? > Diagnostic help > Window description. This opens the XML description of the window. You are only interested in the beginning of the second line of the XML. For example, in the Products function (GESITM) the file starts with:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<WIND NAM="OITM"    ... ... ...


So the very first attribute of the very first tag is the window name you are looking for.

Of course, for a standard object management function like GESITM you already know that the window name is most probably going to be "O" plus the object code. But the above technique might prove useful in case you need the window name of a screen that does not conform to this convention.

Thursday, October 2, 2014

How to implement a birthday reminder - Pt. 2

In the first part of this tutorial we created the screen, window and actions that we need for our birthday reminder. Now we continue with a bit of custom 4GL programming.

In the actions that we created (ZBRD and ZBRDAY) we have standard processing files. Let's write the code for these files.

We create the file ZBRDSTD (standard processing file for the ZBRD action) and put the following code in it:

$ACTION
Case ACTION
    When "OUVRE" : Gosub OUVRE
    When "DEBUT" : Gosub DEBUT
Endcase
Return


$OUVRE
If clalev([AIN])=0 : Local File CONTACTCRM [AIN] : Endif
Return


$DEBUT
For [AIN] Where day(CNTBIR)=day(date$) & month(CNTBIR)=month(date$)
    [M:ZBRD]CNTFNA([M:ZBRD]NBLIG)=[F:AIN]CNTFNA
    [M:ZBRD]CNTLNA([M:ZBRD]NBLIG)=[F:AIN]CNTLNA

    [M:ZBRD]NBLIG+=1
Next

Affzo [M:ZBRD]
Return

In the opening OUVRE action we make sure that the CONTACTCRM table is opened. That is the Contacts (relationship) table. Then, in DEBUT we loop through the records in this table for whom the day and month of their birthday (CNTBIR field) is equal to today's day and month (date$ always contains today's date). After that we display the whole screen with the Affzo instruction.

Next we create the file ZBRDAYSTD (standard processing file for the ZBRDAY action) and put the following code in it:

Subprog BRDAY()
If clalev([AIN])=0 : Local File CONTACTCRM [AIN] : Endif

Filter [AIN] Where day(CNTBIR)=day(date$) & month(CNTBIR)=month(date$)

If rowcount([AIN])>0
    Filter [AIN]

    Local Char SAVACT(GLONACT)
    Local Integer FLGEXE
    SAVACT=GACTION
    Local Char VALBOUT(250), PARBOUT(250)(1..20)
    FLGEXE=1
    GACTION="ZBRD"
    Call SAISIE_CHAR(VALBOUT, PARBOUT, "ZBRD", "ZBRDSTD", "") From GSAISIE
    GACTION=SAVACT
Else
    Filter [AIN]
Endif
End

In this file we filter the CONTACTCRM table to check if we have some contacts whose birthday is today. If we do, we setup and use the SAISIE_CHAR function to call the ZBRD action. As you remember, we cannot use an action that displays a window in a workflow, so we use this two-step procedure - we have an action that is going to popup the birthday reminder on the screen and another action to call the first one. So that is what we just programmed.

The last thing left to do is setup the workflow. So we go to the Workflow rules function (GESAWA) and create and validate the following workflow:

General tab:

Recipients tab: Create only one line, leave all fields empty or with the value of No, except for Type and Recipients:

Action tab:

As you can see, the workflow has Event type: Miscellaneous and Event code: CON. This means that it is going to be executed whenever a user connects to X3. And we have checked the Trigger action checkbox, so the workflow will run the action that we specify in the Action tab - which is our ZBRDAY action, which is going to run our ZBRD action, which is going to display the birthday reminder popup. Maybe a bit complicated... but it works like a charm!

So now when we login to X3, if there are business contacts whose birthday is today, we will see something like this:


Of course, other fields can be added as well - for example, a phone number, so that we can call the contact right away and say Happy birthday!

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.