Programming puzzle in X++: Is this character upper case?

I would like to share a logic puzzle that baffled me today (for some background, read this stack overflow question ). This will likely work in many programming languages, but obviously I am going to show you X++ code.

Problem:
Given the following string, write a job that displays a list of all the characters that are uppercase: AbDtw%@32E

SPOILER ALERT: I discuss my faulty solution as well as a better solution in the following section. If you want to try it first, stop reading here!

My faulty reasoning:

I assumed that you would write a loop that compared each character with it’s uppercase character and if the character is equal to it’s uppercase counterpart, the character must be uppercase. Let’s try that:

  1. static void findCapitalLetters(Args _args)
  2. {
  3.     str testStr = "AbDtw%@32E";
  4.     int i;
  5.     int stringLenght = strLen(testStr);
  6.     str character;
  7.  
  8.     for (i=1; i<=stringLenght; i+=1)
  9.     {
  10.         character = subStr(testStr, i, 1);
  11.         //If the character is EQUAL to it's uppercase counterpart, it must be uppercase:
  12.         if (char2num(testStr, i) == char2num(strUpr(testStr), i))
  13.         {
  14.             info(strFmt("'%1' at position %2 is an uppercase letter.", character, i));
  15.         }
  16.     }  
  17. }

The output:
StrUpr()

OOPS! The characters %, @, 3 and 2 are evaluated as uppercase; this is not what I had in mind. So I assumed (wrongly again) that the answer would be to first check if the character is a letter (if it is a number or symbol, I can just ignore it). I was thinking along the lines of adding str2IntOk()…

The simple solution:
It turns out, if you reverse the question and ask, “Is this character not equal to its lower case letter?”, you get the correct answer:

  1. static void findCapitalLetters(Args _args)
  2. {
  3.     str testStr = "AbDtw%@32E";
  4.     int i;
  5.     int stringLenght = strLen(testStr);
  6.     str character;
  7.  
  8.     for (i=1; i<=stringLenght; i+=1)
  9.     {
  10.         character = subStr(testStr, i, 1);
  11.         //If the character is NOT EQUAL to it's lowercase counterpart, it must be uppercase:
  12.         if (char2num(testStr, i) != char2num(strLwr(testStr), i))
  13.         {
  14.             info(strFmt("'%1' at position %2 is an uppercase letter.", character, i));
  15.         }
  16.     }  
  17. }

The output:
StrLwr()

See how subtle the difference it?

Explanation:
At first the two jobs looked the same to me. I thought the issue was with the way strLwr() and strUpr() work. It wasn’t until I examined the return values that I realized the problem is with the logical operators and not the string funtions.

Consider this table:
UpperLowerCaseTable
If the character is a letter, it has a different value for upper and lower case. If the value is a symbol or number, it doesn’t have an upper or lower case, and the same value returned as upper and lower case.

In the case of numbers and symbols, the question “Is this character not equal to its lower case letter?” has a different answer than “Is this character equal to its upper case letter?”.

Of course, in both cases the real question you should ask yourself is: “What is the problem I am trying to solve?”.

Let me know in the comments if its only me, or if this baffled you for a moment too. Happy problem solving!

X++ “Cross reference” for Base Enums

While I love using the cross reference function in Dynamics AX 2012 (and am training myself to use it more often), the cross reference in our environments are often not up to date. Unfortunately this is especially true for our client development environments.

I wrote a job to find all the table fields that is based on a Base Enum. Simply change the enum name in line 5 to the enum you want to investigate, and the job makes a list of table fields that uses the enum. In my case I only needed tables in the ISV and up, but you can tweak it for all layers, or only one.

For example, for InterCompanyOrigin enum, searching through all the fields in the SYS layer:

  1. //Tina van der Vyver
  2. //makecreatereiterate.com
  3. static void enumCrossReference(Args _args)
  4. {
  5.     EnumName            enumName = enumStr(InterCompanyOrigin);
  6.     EnumId              enumId = enumName2Id(enumName);
  7.     UtilIdElements      utilfield;     
  8.     DictField           dictField;       
  9.  
  10.     setPrefix(strFmt("Enum: %1", enumName));
  11.  
  12.     while select parentId, utilLevel, Name, id from utilfield
  13.         where utilfield.recordType == UtilElementType::TableField        
  14.         && utilfield.utilLevel  == UtilEntryLevel::sys
  15.     {
  16.         dictField = new DictField(utilfield.parentId, utilfield.Id);
  17.  
  18.         if (dictField.baseType() == Types::Enum && dictField.enumId() == enumId)
  19.         {      
  20.             info(strFmt("%1: %2 - %3", utilfield.utilLevel, tableId2name(utilfield.parentId), dictField.name()));
  21.         }
  22.     }
  23. }
InterCompanyOrigin Enum
Output: InterCompanyOrigin Enum

It tends to run quite long, depending on how many fields and layers you include. I’ll see if I can find a way to speed it up in the future.

UPDATE 27 January 2016:
This job does the same for extended data types.

Compile error: Microsoft.Office.Interop.Excel…

A few months ago I wrote a post on how to convert Excel documents to PDF with X++. Well, that piece of code made it into production! :) But I just notice it does not necessarily compile with Ax Build. :(

The code:
Excel Interop error

The error:
The xlApp declaration does not compile because Excel is not installed on the server and AX build can only be completed on the server. If you compile this same piece of code in the AOT on a server that has Excel installed, it compiles without a problem.

Excel Interop syntax error

My solution(s):

  • Install Excel on the server. I am not sure if this will allow AX Build to compile the code, but at least you can then open the AOT after AX build on the sever and compile like I described here.
  • Since I could not install Excel on the server, I did the following:
    1. Compile with AX Build on the server.
    2. If you have a compile error related to Microsoft.Office.Interop.Excel, start the server and open the AOT on a terminal server that has Excel installed.
    3. Compile the code with normal F7 compile.
    4. Compile the CIL on the terminal server.

Hope that helps! Let me know in the comments if you know of a better way to solve this.

Microsoft Dynamics AX Debugging Users group

Here is a quick but useful tip that I learned today:

If you see this when you expected to see the debugger:
Debugging user group

  1. Go to Control Panel -> System and Security -> Administrative tools -> Computer Management
    (To find it quickly, press the windows key on the keyboard and start typing “Computer Management”)
  2. computer management

  3. Click on Local Users and Groups -> Groups. In the middle pane, Click on “Microsoft Dynamics AX Debugging Users”.
  4. Dynamics Ax Debugging Users

  5. Click on the Add button. (Users that are already added are displayed in the members block. Mine are blacked out for privacy.)
  6. Users

  7. In the dialog, enter the name of the user you want to add to the Microsoft Dynamics AX Debugging Users group and click on OK.
  8. Select Users

  9. Sign out and back into the Windows account.

It is possible that only the admin has rights to add users to the Microsoft Dynamics AX Debugging Users group. In that case you will have to log in with the admin account or ask the administrator to complete these steps for you.

Generate a list of shared projects per layer

Today I add to compare the projects in an acceptance environment with the projects in the test environment. I did this so that I can make backups of the projects in test that are not in acceptance yet, before I import the acceptance model store.

Here in the simple job I wrote to show an list of shared projects in an infolog. You can change #ProjectSharedPath to see private projects or UtilEntryLevel::var enum so see projects in other layers.

  1. static void listOfSharedProjects(Args _args)
  2. {
  3.     #AOT
  4.  
  5.     Treenode        shared = TreeNode::findNode(#ProjectSharedPath);
  6.     Treenode        project;
  7.     UtilEntryLevel  utilEntryLevel = UtilEntryLevel::var;
  8.     Str             projectName;
  9.     int             nodeCount;
  10.     int             i;    
  11.  
  12.     nodeCount   = shared.AOTchildNodeCount();
  13.     project     = shared.AOTfirstChild();
  14.  
  15.     for (i=1; i<=nodeCount; ++i)
  16.     {
  17.         if (project.AOTLayer() == utilEntryLevel)
  18.         {
  19.             projectName = project.AOTname();        
  20.             info(projectName);
  21.         }
  22.         project = project.AOTnextSibling();
  23.     }
  24. }

5 Goals for my second year as a Dynamics Ax programmer

Warning:This will be the most uninformative and least instructional post on the blog to date. This post is entirely personal and you can feel free to skip it. Otherwise, read on to be inspired!

Two days ago I posted the 10 things I learned in my first year as an AX Developer. Since then I have been thinking what I would like to focus on and achieve by this time next year. I feel that I need to post my goals here so they are public. This will hopefully add some pressure to accomplish them. In no particular order, I would like to:

  1. Understand Inventory I really find inventory in Dynamics AX difficult. I am not even sure what this entirely entails, but at the moment I quiver with fear when I hear “InventTrans”, “Batch”, “InventDim” or “InventSum”.
  2. Be better at the technical theory(for lack of a better description). I bought Inside Microsoft Dynamics AX R3 a few months ago and have successfully read the introduction. I don’t think I will be able to read the entire book in a year, but by next year, I want to be at least finish with part one.
  3. Ask less help. This sounds like an odd goal; I mean asking for help is completely normal. I am worried that I lean to strongly on others and avoid working independently. This goal is difficult to quantify, but I want to try to write down questions so that I can get an idea what the areas are I am technically or functionally lacking.
  4. Blog at least once a week. This is the goal I am working on this very moment. 😉 Of course, I do not want to write nonsense (like this post) simply to reach one post a week. At the moment I have tons of interesting blog post drafts, Windows Sticky Notes and scraps of paper. I want to work on them and get them out there. If all goes well I would also like to start some series of sorts.
  5. Join a development real live community.So far I have seen mostly groups aimed at Dynamics users. I want to find a group focused on development that physically meets in a city near me. If you know of any, please let me know in the comments!

10 Things I learned in my first year as a Dynamics AX developer

Today marks the end of my first year as a Dynamics AX developer! On the 1st of March 2014 I started working for a Microsoft Dynamics AX partner and I saw Dynamics AX for the first time. I had some development work experience before I started, but that was mostly maintaining very old VB and C# code.

I have learned thousands of things since I started! To keep it brief and celebrate how far I’ve come, here are 10 thing I learned about developing in AX this past year:

  1. I learned to write ternary statements. How I wrote software without understanding ternary statements is a mystery to me now.
  2. Ctrl + Arrow key moves the curser to the next word. This seem silly, but no one every taught or showed me this. I used to click with the mouse or move the cursor character by character with the arrow key.
  3. Developing in Dynamics AX is 90% searching and 10% developing. Even if you need to add one simple line of code, you will most likely spend 30 minutes looking for the correct class, table or form method to add the line, and then spend less than 3 minutes writing the code.
  4. It is much harder to find solutions and answers with Google. Not all of my colleagues agree with me on this, but I think it is because they have never googled for a Java or C# issue. With more popular languages you have a much bigger chance that someone had the same problem as you.
  5. X++, C#,  Java compared

  6. Since it is more difficult to find recourses and beginner guides, the easiest way to learn is from someone with more experience. I have completed Microsoft Development training and have read blogs, books and forums. They were all great, but I have learned the most from working one on one with a senior developer.
  7. No human being should be working on a computer without Ditto Clipboard Manager. This includes people who uses computers to write email and write in Word. I dare you to try it. It will change the way you copy and paste forever and make you wonder why Windows 1998 wasn’t released with a clipboard manager.
  8. Ditto

  9. It pays to write neat code. Write curly braces for every if-statement, indent properly and run a best practice check often.
  10. Triple check code when you copy methods from the sales to purchase or customer to vendor. Also, you should only be doing this if is impossble to use a map like SalesPurchLine or SalesPurchTable. A senior emailed me this screenshot.. oops!
  11. SalesPurchCopy

  12. Writing SQL like statements in X++ is simple and intuitive. I hoped this was a deprecated Axapta relic when I saw it the fist time, but know I know how simple and effective it is to find data in the database.
  13. I have learned that I still have a lot to learn. Working with extremely competent people with years of experience have taught me that this is only the beginning.

What is the name of the database?

I rarely need to work with the AX database. In my company developers work on the AOS, and leave SQL related thing to the BI and installation people. Therefore, if I want to see the name of the database that an AX installation uses, it means I am either doing something I should not be doing, or are really desperate..

..and then I can never find the form where you can see the name of the database!

For future references (and to shorten those moments when you are not sure if you restored the correct database), to see the name of the database that contains the data, go to:
System Administration > Inquiries > Database > Database information
Database

This opens a form with the server name and database:
Database2

If you are a hardcore developer who prefer to dazzle others with your AOT skills, you can find this form at AOT > Forms >
SysSqlStatus

.

How to add a range or data source to a form data source

Adding a second datasource with X++, or a range to a form datasource should be simple, yet today I spent ages trying strange things with multiple records. In the end it took me, two senior developers and a existing example in Dynamics AX to get to this simple solution. It might look silly, but I am definitely documenting this one for future reference!

I used to do the same thing by adding code in the form’s class declaration and the data source’s init and executeQuery methods. That is way too complex. Just add the code below to the data source’s init. Also, make sure you do this after the super() call, otherwise this query will not be initialized.

Add a range to a form
Suppose you have a form, with a PurchLine datasource added as a node in the AOT:

FormDatasource

If you only want to see purchase lines that are linked to open orders, then you need to add a range with PurchStatus equal to Backorder. You create this range in the data source’s init method:

  1. public void init()
  2. {
  3.     QueryBuildDataSource        qbdsPurchLine;
  4.  
  5.     super();
  6.  
  7.     qbdsPurchLine = this.query().dataSourceTable(tableNum(PurchLine));
  8.     qbdsPurchLine.addRange(fieldnum(PurchLine, PurchStatus)).value(queryvalue(PurchStatus::Backorder));
  9. }

Add an additional datasource to the form
Now suppose you want to filter the PurchLine based on some criteria on the PurchTable. For example, you only want to see lines from derived intercompany orders. (This is just an example, PurchLine actually has it’s own intercompany origin field.) You could add an additional PurchTable datasource to the form, and join the datasources in the form properties. Every easier is adding the following to the init of the PurchLine datasource:

  1. public void init()
  2. {
  3.     QueryBuildDataSource        qbdsPurchLine;
  4.     QueryBuildDataSource        qbdsPurchTable;
  5.  
  6.     super();
  7.  
  8.     qbdsPurchLine = this.query().dataSourceTable(tableNum(PurchLine));
  9.     qbdsPurchLine.addRange(fieldnum(PurchLine, PurchStatus)).value(queryvalue(PurchStatus::Backorder));
  10.  
  11.     qbdsPurchTable = qbdsPurchLine.addDataSource(tableNum(PurchTable));
  12.     qbdsPurchTable.addLink(fieldNum(PurchTable, PurchId), fieldNum(PurchLine, PurchId));
  13.     qbdsPurchTable.addRange(fieldnum(PurchTable, InterCompanyOrigin)).value(queryValue(InterCompanyOrigin::Derived));
  14. }

You can declare as many QueryBuildDataSource variables as you need, and add them like the PurchTable above.

X++ syntax highlighting with WP-GeSHi-Highlight

While I was setting up this blog, I installed WP-GeSHi-Highlight for X++ syntax high lighting. I wrote about it here. At the end of that post I wrote that I would edit the CSS of the plugin to make it look more like the X++ Editor.

I make a few small changes, this is what I have come up with so far:

  1. /// <summary>
  2. /// This job is used as an X++ sample (From Wikipedia's Microsoft Dynamics AX page)
  3. /// </summary>
  4. public static void xppTest3(Args _args)
  5. {
  6.     UserInfo userInfo;
  7.     ttsbegin;
  8.     select forupdate userInfo
  9.         where userInfo.id != 'Admin'
  10.            && userInfo.RecId == 1234567890;
  11.     while (userInfo)
  12.     {
  13.         userInfo.enable = NoYes::No;
  14.         userInfo.update();
  15.         next userInfo;
  16.     }
  17.     ttscommit;
  18. }

and this is the same code in the MorfX editor in 2012.
X++ code example

The original CSS produced this result:
CSS

I removed the dot from the line numbers and make the numbers turquoise. I also made the line height less. If you by chance use WordPress to write about X++ and have the WP-GeSHi-Highlight plugin, you are welcome to the changes I made to wp-geshi-highlight.css. The changes are small, but it just makes me feel a bit more at home with the way the code is displayed.