Archive for the ‘Utilities’ Category

Compare 2 records

Monday, October 10th, 2011

Hi everyone,

i’ll present here a pair of tips to compare two record variables to check if they are the same, and i’m not talking about that they’ve the same key, but that they are totally equal, every field.

Actually, such a function does not exist in NAV (it’s not built-in, at least). Instead, I found a pair of methods to check if the records are equal. Both methods have their pros and cons, so I’ll post them both.

Then, I’ll explain my observations and it will be up to you to choose the best one for your specific case.

Code Snippet

IsDifferent function: (the second function)

Inspired (ok, almost copied :) ) from function logmodification of codeunit 423 - Change Log Management.

This function compares all the NORMAL fields one by one, and if the value is different, it returns false.

IsDifferentWithFormat function: (the first function)

This one is all mine. :P It uses the format function on the recordref variables and compares the results.

It uses an array of 5 text1024 and compares the results 1024 by 1024 characters. If both the format of rec and xrec are blank, it means we have reached the end of record, so the function exits false (records are equal).

I did some tests on performance of both these functions, and it turned out that usually the fastest is the one with FORMAT instruction, due to the minor number of loops. The other function is faster only if it’s one of the first fields that is different (or if the table has just a few fields).

The implementation is the same for both functions, (just pass 2 evaluated recordrefs to compare) so it’s up to you to choose the best function for you! THIS DOESN’T WORK, UNFORTUNATELY (SEE COMMENTS), STICK TO ISDIFFERENT FUNCTION FOR LONG TABLES (BUT IT WILL PROBABLY TAKE A LOT OF TIME), AND USE FORMAT(REC) ON A NORMAL TABLE VARIABLE FOR SMALL TABLES.

I’m going to upload the objects ASAP, now I’ve to go back to work.

See you next time!

Silent or Unattended Installation of NAV 2009

Thursday, April 28th, 2011

Hi everyone,

I’ve recently followed a nice 1hour-livemeeting about silent installation of microsoft dynamics nav 2009 R2 (or, as MS relator says: “microsoft dynamics EN-EI-VI two thousands and nine, AR two”).

First of all: about half of the meeting have been wasted in saying the product name :)

But let’s get into the good part.

WHAT IS SILENT OR UNATTENEDED INSTALLATION

Maybe a lot of you already know it, but for completeness, i’ll explain it here in two words: basically a silent installation is exactly what you can imagine. A software (in our case nav2009) is installed on a client pc without user interface and user interaction (that is: no “next->next->…->finish” procedure).

This is quite useful if you want to spare time during client installations, especially if there are a lot of client PCs being involved.

INTRODUCTION

Microsoft recommends to only run “setup.exe” file when you install a new NAV client (and/or server), because the installer have been tested by them using this procedure only. This means that you shouldn’t run the various .msi files individually (althought possible ;) ).

Anyway, the setup.exe file runs all the .msi files that are necessary for a correct installation. It also installs the prerequisites, for example .NET framework3.5, and other stuff.

This means that our goal is to run this exe silently.

RUN THE EXE SILENTLY

Nothing simpler! there are some parameters that you can append to the command line in order to achieve fancy things:

/quiet <– you can guess that we need this to run the exe in background

/log  [filename] <– create a log textfile in the specified position

/config [filename]<– another key feature we need to configure our installation automatically

/uninstall <– i’m still wondering what this will do ;)

/repair <– repairs a bad installation

LET’S INSTALL!

First of all, let’s start our exe file by doubleclicking it, as always.

Then let’s go through all the steps of the installation, select the pieces of software we want to install (classic, roletailored, server, etc) and then STOP(!) at the very last screen of our installation wizard: DON’T PUSH FINISH!! Click instead on one of the links of the configurable parts, for example “Role tailored client” and go ahead with some further configuration.

(in the screenshot, you can find my configuration. The blacked values are all my computer name. Because i’m going to install my standalone dev environment).

 My install screen

You can guess what we’re going to do now: click on “Save Configuration Settings” and create an xml file. You can open this xml file and check what it contains: a list of all the product components and how to behave with them (there are actions like local, absent, repair, bla bla bla…). Why do we need this file? In order to avoid user intervention during installation, obviously! The xml will be the [filename] argument of our “/config” parameter.

Now, you can run the silent install by simply doing start -> run -> [exepath] /quiet /config [configfilepath.xml], but there’s a much more smart way to do it: create a bat file that tells you that the installer is working, and alerts you when it has finished with a message. In this way, you can deploy the file on various PCs, and start it. In order to achieve this simple task, just copy the following text in a notpad and save it as .bat instead of .txt. 

[exepath] -> where setup.exe is placed: simply write setup.exe if the but is in the same folder of the setup.exe ;)

[configpath] -> where you have saved the configuration xml file .

[logpath] -> where do you want (if you want it) the log file path saved. I always recommend to create this log.

Remember to write file extensions, too, and also place the paths between the double quotemarks if the one of they contain spaces or special characters! ;)

@echo off
Echo start INSTALLING NAV2009R2: IT WILL TAKE SOME TIME, PLEASE WAIT...
Time /t
[exepath(.exe)] /quiet /config [configpath(.xml)] /log [logpath(.txt)]
Echo end: INSTALLATION COMPLETE! YOU CAN NOW UNLEASH THE PWR OF NAV2009R2!!!
Time /t
pause

The installer will take much more time if it’s the first installation, because the prerequisites are huge. The nav installation alone will take only a few minutes, instead.

And that’s it! you can create as many configuration files as you want. Be sure to take a look to the log file, too and keep in mind that the root folder of the installer shouldn’t be too “deep” in your file system. I received some errors during my tests which disappeared after moving the root folder of the installer under “C:\” …weird, huh? (unless i’ve messed up something with the installer) O.o

Happy Install!

HyperList - How to run filtered list pages from report hyperlinks!

Monday, January 10th, 2011

Hi readers,

It’s passed some time since i found this workaround, but hey, better late than never! ;) : as you can read from the topic title, we’re going to do something that’s actually impossible in nav, because the “&filter=” parameter is not accepted for hyperlinks that refers to pages. Here’s a sample:

if you try to run this

dynamicsnav:////runpage?page=9&filter=Languages.Code:101011

you’ll get back a big and smiley error: “The query parameter is not supported.”

I was very disappointed when i noticed it: what if i wanted to click the “Balance (LCY)” field in the customer  - top 10 list?

I started to dig the help online and i noticed that report DOES accept the “&filter=” parameter instead, and that night i’ve had the idea: filter the table i want to show, pass its filters to a report, copy the filters in the report and then run the filtered page. YEAH!

Now, I’m going to explain what i’ve done, but first of all, you have to know how to run generic hyperlinks from a report to another report. Here you can find all the necessary:

http://msdn.microsoft.com/en-us/library/dd338628.aspx

IMPORTANT: I have to credit mibuso member “einstein.NET” for some important hints about the function that parses the “GETVIEW”. Thanks a lot!

I’ll take the good old Inventory - Top 10 List as a sample: it has an Inventory field and i want that field to behave just like the drilldown on the item card. Let’s start.

First of all, let’s create this simple report (50500 in the fob file): HyperList Report. This report has the only use to run a page based on the filters of the dataitem. let’s set the Item ledger entry table (the table on wich the inventory flowfield is based on) as a dataitem of indentation zero. You will notice that in my 50500 report there’s also a cust. ledger entry dataitem: this is because you can add as many dataitems of indentation zero as you want (up to the max number of dataitems that a nav report allow) in order to reuse this utility report for as many drilldowns as possible.

The report Must be processingonly, and userequestform must be set to no (for obvious reasons :) )

then write the following lines of code in the report

hyperlist report code

the
IF GETFILTERS <> '' THEN BEGIN
line of code is used to “select” the dataitem to run: obviously, if you didn’t set any filter the report doesn’t work, but if you’ve not set filters, you also don’t need to use this report ;) .

Now that our report is ready, we have to run it with the correct filters in order to let it run the correct page with correct filters for us; to achieve our goal we have to do some easy steps.

At first we have to modify our report Inventory - Top 10 List, adding a rec variable for item ledger entry table. Then, we are going to set all the filters we want. In our case, i put them in the integer dataitem (onaftergetrecord) after the item.get is done: this is the correct place because (as you can see in the section designer) the integer table is the one being printed.

filters.JPG

Also add this line of code in the onprereport, that initializes some parameters in the “jump to url” codeunit
CUJumpToUrl.FNTFirstRun;

firstrun.JPG

and don’t forget to set “enablehyperlinks” property to yes in the report

now, we will have to call the “Jump to url” codeunit i added in the .fob: we are going to call it directly in the sourceexpression of a textbox in the sections, like this:

sections.JPG

CUJumpToUrl.FNTCreateUrl(1,REPORT::"HyperList Report",CUJumpToUrl.FNTAppendFilter(DATABASE::"Item Ledger Entry",TBItemLedgEntry.GETVIEW(FALSE)))

Now, i’m going to explain this call. Here’s the code of the codeunit:
jumptourl.JPG

1: indicates that you want to run a report; if you pass zero, you’ll run a page instead.

REPORT::”HyperList Report”: this is the report we created before for the purpose of running pages.
CUJumpToUrl.FNTAppendFilter: this is a function that returns a string with the needed filters
DATABASE::”Item Ledger Entry”: this is the base table for the drilldown
TBItemLedgEntry.GETVIEW(FALSE):the getview returns a string with all the applied filters. this string is parsed by the FNTAppendFilter function to make it suitable for the link.

now that we have created our link, we can open the rdlc designer, and place the value in the “jump to url property” of the inventory field. add some fancy formatting to the textbox and we’re done!

preview.JPG

UPDATE: you can also use dummy filters to pass values to the hyperlist report and eventually run functions within the pages. this is useful to run fancy pages that needs a sort of “getparameter” function before being run. Here’s a sample:

Sales Shipment Header - OnPreDataItem()
IF GETFILTERS <> '' THEN BEGIN
  EVALUATE(PostingDate,GETFILTER("Posting Date"));
  mypage.GetParameters(
  GETFILTER("Patient Code"),GETFILTER("Responsibility Center"),GETFILTER("Sell-to Customer No."),PostingDate);
  mypage.RUN;
  CurrReport.QUIT;
END ELSE
  CurrReport.BREAK;

Sample Objects

Run RTC Reports From Classic

Wednesday, November 3rd, 2010

hi everyone, i’m back after some time with a new and easy trick for the laziest programmers…

when developing reports for the 3 tier client, we are going to make a lot of tests because of their complexity and because they are error prone.

This approach lead us to make something like 50-100 runs per report in order to check the layout after every change. Unfortunately we would need an action to run our report. I found an easy and simple way to make the whole thing faster:

create a new page with only a container, and put this code in the onopen trigger
REPORT.RUNMODAL(REPORT::"myreportname");
COMMIT;
ERROR('');

Now, the only thing you have to do is to run the page: the commit makes sure that if your report is modifying something, it will be saved. this is necessary because we have the ERROR(”) that closes the page (the currpage.close does not work) that would lead to an unwanted rollback.

It’s a quit stupid trick, but saves some time in day to day work.

Enjoy

Is it really temporary?

Thursday, September 16th, 2010

Temporary tables are a very useful tool we have in nav, and their usage is constantly increasing in my projects. For this reason, during upgrades (or when someone else have to modify my code), there’s the chance that some BAD guy removes the temporary property, just for mistake or because he doesn’t know what it is O.o

The real problem is when i do

MyTempTable.RESET;
MyTempTable.DELETEALL;

In order to clean it before some further processing. What does it happen if the table variable is not temporary?!
You (or him) get simply nuked by the customer, because you (him) nuked his table!

How to avoid this bad situation (i mean, getting nuked)?
RecRef.GETTABLE(MyTempTable);
IF NOT RecRef.ISTEMPORARY THEN
  ERROR('MyTempTable must be temporary!What are you doing?You wanna die?');

I thought to adapt this code to any table, because i wanted to have a function where i pass a record variable and then execute the code without rewriting it everytime, but up to now, it’s not possible in nav :(
Anyway, it’s not so much effort to put these lines of code in your application, unless you’re even lazier than me :)
just declare a recref variable, copy/paste the code and modify the error text!
See you next time!

Payment Lines (Only Italian Version) and CalcDateBOC

Friday, February 19th, 2010

Payment lines is an Italian localized table that allows to manage (and post) payments at different due dates.
This table is automatically filled for each document when you insert the payment term in the header; you can see this table in every document by clicking Function->Payments.

CalcDateBOC is instead a useful function i recently discovered in codeunit 7600, which, in few words, does the native CALCDATE function, but based on the calendar, that is, skipping nonworking days (i won’t explain this codeunit further, as i’ve not completely understood it, it has 9 parameters).

Getting Started:
- Create a base calendar, and set at least the weekly nonworking days: to achieve this, go to the base calendar form (you can access it from Administration>Application Setup>General>Company information>Shipping tab>Base calendar code field (click lookup)>Shift+f5 to go to the card)
-Create a calendar by setting a name of your choice
-Click Functions>Maintain Base Calendar. Here you can select the recurring method etc…it’s user friendly, i don’t think i have to explain you how to do it. Set only saturday and sunday holidays for our purpose.
- Assign the calendar you just created in the Base calendar code field of the Company information table.

This table is really easy to use and if we create a payment term like 30/60/90 Days, the result will be 3 payment lines:

one with “due date” = “sales header”.”document date” + 30D,
one with “due date” = “sales header”.”document date” + 60D
and one with one with “due date” = “sales header”.”document date” + 90D.

Ok, pretty straightforward, up to now…
BUT MY CUSTOMER WANTS TO BE PAYED THE FIFTEENTH WORKING DAY OF THE MONTH AFTER THIS!!
mmh, I can use my beautiful calcdateboc for this, can’t I?well, not really.

The calcformula i would use in this case is ‘<CM + 15D>’ (remember the brackets), and i would pass it to the calcdateboc Function:
GMANewDate := CUCalcDate.CalcDateBOC('',MyDate,0,CompanyInfo."Base Calendar Code",'',0,CompanyInfo."Base Calendar Code",'',FALSE);

With the calendar we just set, if we suppose that MyDate is First January 2010, the result will be the 8th march 2010.
What happened???well, the function worked literally as expected: with CM, nav will calculate the number of days to the end of the month, adding them to the base date…but skipping saturdays and sundays!
The result is that CM won’t bring our date to the end of the month, but X days after, where X is the number of holidays from today to the end of the month.
To avoid this problem we should do a plain CALCDATE on the “CM” part of dateformula and a CalcDateBOC on the “15D” part of the formula.

What i’ve do to maintain this?Here you are: 

- Add 2 fields to table 12170:
—Date Calc. to Add > DateFormula
—Date Calc. to Add (Working) > DateFormula
- Add these fields to forms 12170 and 12171:

Add this code under Sales Invoice table: “payment terms” - OnValidate Trigger, right after **PaymentSales.”Payment %” := PaymentTermsLine.”Payment %”;** line of code.

OLD VERSION

//Copy Values of new fields
PaymentSales.”Date Calc. to Add” := PaymentTermsLine.”Date Calc. to Add”;
PaymentSales.”Date Calc. to Add (Working)” := PaymentTermsLine.”Date Calc. to Add (Working)”;
//store the plain CALCDATE Part
GMANewDate := CALCDATE(PaymentTermsLine.”Date Calc. to Add”,”Document Date”);
//This “IF” is explained later
IF FORMAT(PaymentSales.”Date Calc. to Add (Working)”) ” THEN BEGIN
//Add to the previously CALCDATEd date the “Working days” part
GMANewDate := CUCalcDate.CalcDateBOC(”,GMANewDate,
0,CompanyInfo.”Base Calendar Code”,”,0,CompanyInfo.”Base Calendar Code”,”,FALSE);
END;
//Evaluate the standard “due date Calculation” field as the difference between the final calculated date and the source date
//(”Document date”, in NAV Standard)
EVALUATE(PaymentSales.”Due Date Calculation”,FORMAT(GMANewDate - “Document Date”) + ‘D’);
EVALUATE(PaymentTermsLine.”Due Date Calculation”,FORMAT(GMANewDate - “Document Date”) + ‘D’);

NEW VERSION
- Create this function in Payment terms Table (12170)

FNTCalcDays
//PARAMETERS:
//pGMASourceDate: starting date for calculation(e.g. document date)
//pTBPaymTermsLine: line of “payment lines” table to use for cacluation
//RETURN VALUE:
//text50
lTBCompanyInfo.GET;
lGMANewDate := CALCDATE(pTBPaymTermsLine."Date Calc. to Add",pGMASourceDate);
IF FORMAT(pTBPaymTermsLine."Date Calc. to Add") '' THEN BEGIN
lGMANewDate := lCUCalcDate.CalcDateBOC('',lGMANewDate,
0,lTBCompanyInfo."Base Calendar Code",'',
0,lTBCompanyInfo."Base Calendar Code",'',FALSE);
END;
EXIT('');

-Substitute the previous version’s code with this:

PaymentSales."Date Calc. to Add" := PaymentTermsLine."Date Calc. to Add";
PaymentSales."Date Calc. to Add (Working)" := PaymentTermsLine."Date Calc. to Add (Working)";
EVALUATE(PaymentSales."Due Date Calculation",PaymentTermsLine.FNTCalcDays("Document Date",PaymentTermsLine));
EVALUATE(PaymentTermsLine."Due Date Calculation",PaymentTermsLine.FNTCalcDays("Document Date",PaymentTermsLine));

BE AWARE THAT AFTER THIS MODIFICATION, DUE DATE CALCULATION BECOME USELESS, AS WE WILL HAVE THE SAME RESULT BY ONLY SETTING THE “DATE CALC TO ADD.” FIELD.

After evaluating the standard field, nav can proceed alone as always…no further code to modify!
 

And now let’s test it and explain that “IF” statement

TEST 1: 15th day of the next month
no problem here: From the sales invoice, invoicing tab, click the lookup of “payment terms code” field and then>Calculation. You’ll see the payment term calculation setup (it’s one of the two forms we’ve modified before: no. 12170).
here you have to set “date calc to add.” = CM+15D. Now try to set this payment term in your invoice and you’re done.

TEST 2: 15th WORKING day of the next month
no problem here, too: From the sales invoice, invoicing tab, click the lookup of “payment terms code” field and then>Calculation. You’ll see the payment term calculation setup (it’s one of the two forms we’ve modified before: no. 12170).
here you have to set “date calc to add.” = CM AND “date calc to add. (working)” = 15D. Now try to set this payment term in your invoice and you’re done.

TEST 3: 15th day if it’s a working date; otherwise the day after, and so on…
Here’s where the IF come in hand: in the first test, calcdateBOC is not run, as the “IF” fails (we didn’t set “date calc to add. (Working)” field).
But now we need to calculate first working date from the fifteenth of the month: let’s put CM+15D in our “date calc to add.” field, and then a ‘0D’ in the “date calc to add. (Working)” field. In this way the “IF” becomes true, and function CalcdateBOC is run correctly, calculating some more days after the fifteen, (if the 15th is an holiday).

Considerations

After this implementation, the due date calculation (standard field), become “user-transparent”: in my personal implementation i’ve hidden in order to not confuse the user. It’s function can be replaced by the new “Date calc to add.” field, that does exactly the same work as “due date calculation” if you don’t set the “Date calc to add (Working)” field (see TEST1

Sorry, i know that my posts are not well edited, but i have to take some confidence with “WordPress” :)

I’ll edit this post soon, with a solution using a function in order to reuse the code i put in the sales header table, more “user friendly” field names and as always, cronus italy FOB&TXT…stay tuned! Well, i did it :)

HERE ARE THE SAMPLE OBJECTS (FOB&TXT)

Sample Objects

Number Series Renew

Friday, January 15th, 2010

Sometimes new year means new no. series to create. Booooring…

To make this thing faster and less crappy for me or for a customer that does this alone, i’ve created this form (it’s rude, i know, but i’ve done it in 2 hour one morning after a week of holidays). I have to thank Michal K. from Czech Republic (a key user), who inspired me to do this.

And now, i’ll go for the explanation: the form only works in versions 5.00 onwards, because i use property “sourcetable temporary”. I load the form with the last No. series line of each No. serie: in this way the user does not have to browse no. series table and drill down through each record for each No. serie he wants to update. Moreover, the user can press F3 to insert a new line right under the no. serie he wants to update, copy the value of the no. serie with F8 and then populate the necessary fields:

-”Line no.” is automatically set to last line “line no.” + 10000
-”starting date” is automatically set to last line “starting date” + 1Y.
-”starting no.” is automatically set to last line “starting no.”

In short, the user can insert a new number series by clicking F3, F8, edit the number, close the form. (multiple lines can be created, for sure). Also, existing no. series lines can be modified.
Thanks to the onvalidate triggers on the standard no. series table, all the business logic is correctly executed while inserting/modifying lines.

As i said, the table being modified in the form is only temporary, but if you take a short look to oncloseform trigger, you’ll notice that i flush the temptable to the real table.

I’ll also provide a <5.00 version (it also includes a codeunit to populate the temptable): No Series Renew for previous versions

I hope this form will be useful for you, and now, let’s get back to my work…have a nice day

Exchange Rates Handler

Wednesday, January 13th, 2010

I have had to deal with exchange rates, and a thing I found really boring is to manage all the cases below:
if i start from LCY amount and i put it on FCY amount then use function exchrateLCYtoFCY
if i start from FCY amount and i put it on LCY amount then use function exchrateFCYtoLCY
and so on….
the function i post here, receive the six parameters, and depending on the values of CDFromCurrency and CDToCurrency decides what is the function to run (and if it must be run). At the end, it rounds the value to the correct amount.
Here are the input parameters with a little explanation:

-vDECAmount: amount to be translated. if 0 nothing happens
-vcdfromcurrency,vcdtocurrency: self documenting…as always, blank is LCY. If they’re the same, only rounding is performed
-vgmadate: date reference to calculate the conversion factor
-vDECCurrFactor: if this is =0 or =1, the standard function ExchangeRate is calculated, otherwise, the input value becomes the currency factor
-vTFUnitAmount: true if the result have to be rounded with unit-amount rounding precision, otherwise the amount will be modified with amount rounding precision

i’m trying to understand how to post code in a good way :)…well, after some tries, i think i cannot post it correctly, so i’ll upload the files…if you don’t want to dowload the files here Exchange Rates Utility, just go to this topic in mibuso ;)
Enjoy!