Skip to content

Reduce, Reuse, and Recycle

Every Friday morning, my neighbor simply amazes me.  Friday is trash pickup day. When I look outside at the end of my driveway sits our jumbo sized big green Herby Curby for our household of 3 so full that the lid won't close.  But at the end of the neighbor’s driveway they have a small blue trashcan for their family of 4.  I finally had the chance to ask him one day how they could get by with such a small trash can each week, and he explained that they recycle all their paper, plastic, and glass.  They have bins in their garage that they take once a month to the recycling center in the next town. 

Image of computer keyboard with Recycle logoI believe in being good stewards of the beautiful planet God gave us, and I also believe in being a good steward of the NAV databases our clients allow us to work in.  Because of that, sometimes we have to pull back on our urge to create new code and reuse the code that is already there.  As one hillbilly said to another, “Just because you can reproduce doesn’t mean you should.”

There are several benefits of recycling and reusing code.  If you find a function in standard NAV objects that you can call from your applications and repurpose, you reduce the amount of code you are writing and the time it takes to test your modification.  Reducing the number of custom code lines injected also reduces the time needed to upgrade your database when the time comes to do that.  It also gives you cleaner code.  No one wants to travel behind a developer that has created the same routine 5 times to do the same thing. 

Before you begin a NAV task, analyze the coding tasks.  I usually break this down into object types.  What table changes will you need to do?  What forms will need to be changed or created?  Are there any reports that are required?  You get the idea.  Once you’ve completed that task, review your list and ask yourself if NAV has something similar that could be repurposed or reused.  Think about what you are trying to accomplish and the various applications in NAV.  Are there any applications in NAV that already do what you are going to do?  Likewise, think about the code you will need to write and if there would be any chance in the future that you'd need to do the same thing in another task.

I recently had a perfect example of this to come across my desk.  In working with a custom table of items and item information, I needed to set a flag on the record to determine if the record had changed or not.  Sounds simple enough, right?  Well, here was my developing delima.

NAV does not allow:

If CustomItem <> TempCustomItem then….

That doesn’t fly with NAV.  I could do something like this:

If (CustomItem.field1 <> TempCustomItem.field1)  and
   (CustomItem.field2 <> TempCustomItem.field2)  and
   (CustomItem.field3 <> TempCustomItem.field3)  and
   (CustomItem.field4 <> TempCustomItem.field4)  and
   (CustomItem.field5 <> TempCustomItem.field5)  and
   (CustomItem.field6 <> TempCustomItem.field6)  and
   (CustomItem.field7 <> TempCustomItem.field7)  and ...       

But I quickly realized this would mean continual modifications when new fields are added to the table in the future, which is not something I could easily communicate to a developer who might one day follow my work.  I also realized my fingers would fall off before I completed the project! This, my friends, is what I would refer to as "Herby Curby coding".

So, think with me now, where in NAV would you find that it compares one record to another?  Don’t read ahead – think about it.

Do you know?  If so, give yourself a gold star! 

I found a function in Codeunit 423 Change Log Management.  One of the things the Change Log Management granule does is determine if a record has been modified so that it knows when to insert it in the change log, along with who changed the record and other information.  The function is called “LogModification” and it looks like this:

LogModification(VAR RecRef : RecordRef;VAR xRecRef : RecordRef)
IF NOT IsLogActive(RecRef.NUMBER,0,1) THEN
  FldRef := RecRef.FIELDINDEX(i);
  xFldRef := xRecRef.FIELDINDEX(i);
  IF IsNormalField(RecRef.NUMBER,FldRef.NUMBER) THEN
      IF IsLogActive(RecRef.NUMBER,FldRef.NUMBER,1) THEN

The function uses two variables: RecRef and xRecRef, which are of type RecordRef.  It receives in two records, compares them to determine if they are the same, and based on that result may write the change to the Change Log Entry table.

Of course in my code, I don’t need to use the Change Log Entry table, nor most of the code in this function.  So I cannot simply call it from my code.  However, it is a fine example of how to compare two records in NAV without a lot of fuss and muss!  Based on this example, here’s what I developed:

In the report that needs to compare the two records, I added the following code:

RecordsMatch := CustomFunctions.CompareTwoRecords(RecRef1,RecRef2);
IF RecordsMatch THEN
  CustomItemStatus := 'UPDATE';

RecordMatch is a boolean variable that is returned from a function called CompareTwoRecords that is in a codeunit of custom functions called (eh-hem…excuse my simplicity) “Custom Functions”. 

TIP: When creating custom functions, think about the possibility of it being reused.  If you think it might ever be needed again - don’t bury it in your code.  Create a codeunit of nothing but custom functions that you reuse.  Then when you need these functions, you don’t have to search your entire database for where they might be located.

The function CompareTwoRecords looks like this:

   CompareTwoRecords(RecRef1 : RecordRef;RecRef2 : RecordRef) RecordsMatch : Boolean
      FldRef1 := RecRef1.FIELDINDEX(I);
      FldRef2 := RecRef2.FIELDINDEX(I);
      IF FldRef1.VALUE <> FldRef2.VALUE THEN


The function receives in two RecordRef datatype variables, RecRef1, and RecRef2.  Using the RecordRef FIELDCOUNT function, it first determines how many fields are in the first record.  Based on that count, it sets an index (I) and uses that to cycle through the fields, setting an index on each record using FIELDINDEX, and passing the fields to FieldRef datatype variables FldRef1 and FldRef2.  It then compares the VALUE of the two fields (FldRef1.VALUE <> FldRef2.VALUE).  If at any point the comparision finds differences, the loop ends and the function exits with FALSE being the value of RecordsMatch.  If it gets completely through the field looping without exiting, RecordsMatch is set to true, indicating that there were no differences in the two records.

Notice how with RecordRef and FieldRef data types you are not defining the records being compared.  I can reuse this function ten thousand times by just reassigning the records I want to compare to RecRef1 and RecRef2.  The function is completely reusable.

TIP: Notice also that when you write good code, C/Side isn’t the only language used.  Put some English in your code for documentation.  I once worked with a developer (who will remain anonymous…unless you ask) that said, “If it was hard to write, it should be hard to read.”  Unfortunately, that’s not true, and fortunately he didn't really code like that.  You should always write your code with the next developer in mind.  That will ensure you’re building integrity into the future modifications that may be needed in your custom code.

All Codeunits in NAV are rich in examples of C/Side code.  When I first started developing in NAV, I read Codeunits 80 (Sale-Post) and 90 (Purch.-Post).  Not only will you begin to understand C/Side, you’ll learn about the two most commonly modified areas of NAV.

The Challenge

This week I have a challenge for you! Yes, that’s right, another opportunity to earn a gold star! 

If you’re new to NAV Development:

Think about NAV and the things it does.  Name one function that you can find a way to recycle. 

If you’re a seasoned Developer:

Give an example of a function you’ve recycled from Standard NAV code or a function you’ve written that you often reuse.

I’d love to see these in the comments on the blog (see that little “Log in or register to post comments” hyperlink below?)

Until next week, be good stewards of your databases and reduce, reuse, and recycle!



Blog Tags: 



I've never taken the time to convert this one to use record refs, but it could easily be done and reused. As it stands I usually just change the one record variable along with the names of the variables.

There are typically two methods for brute forcing a set of records you want to process. The first is building a really long filter string of every individual record: Item1|Item2|Item3|....|ItemN

The second is inserting the records into a temporary table. I still like this method the best, but the method below can be reused for the purpose.

The function will build a perfect filter string from your selected records. Like a lot of things it has its limitations, but it should be in everyone's utility bag.

GetSelectionFilter() : Code[80]
ItemCount := Item.COUNT;
IF ItemCount > 0 THEN BEGIN
  WHILE ItemCount > 0 DO BEGIN
    ItemCount := ItemCount - 1;
    FirstItem := Item."No.";
    LastItem := FirstItem;
    More := (ItemCount > 0);
    WHILE More DO
      IF Item.NEXT = 0 THEN
        More := FALSE
        IF NOT Item.MARK THEN
          More := FALSE
        ELSE BEGIN
          LastItem := Item."No.";
          ItemCount := ItemCount - 1;
          IF ItemCount = 0 THEN
            More := FALSE;
    IF SelectionFilter <> '' THEN
      SelectionFilter := SelectionFilter + '|';
    IF FirstItem = LastItem THEN
      SelectionFilter := SelectionFilter + FirstItem
      SelectionFilter := SelectionFilter + FirstItem + '..' + LastItem;
    IF ItemCount > 0 THEN BEGIN


Very nicely done, Matt!  This would be a great tool for many purposes.  I have a client that allows their salespersons to view a list of the items the customer most commonly orders.  This would be a great way to take that one step further and filter items selected to pass into a reorder.   You get a gold star!


A simple one, but whenever i am importing data from a file this is one of my best friends. It has a string with Data that is seperated with, for example, a semicolon. This functions gives me the values in an array so they are easier to access.
    PROCEDURE fnk_SeperateString@1000000001(par_te_String@1000000000 : Text[1024];par_te_Sign@1000000001 : Text[30];VAR par_te_Array@1000000005 : ARRAY [100] OF Text[1024]);
      lo_in_CodePos@1000000002 : Integer;
      i@1000000004 : Integer;
      lo_bo_Finished@1000000003 : Boolean;
      // *** Separates a "Sign"-Separated String in an Array  ***

      i := 1;

      lo_in_CodePos := STRPOS(par_te_String,par_te_Sign);

        IF lo_in_CodePos > 0 THEN BEGIN

          par_te_Array[i] := COPYSTR(par_te_String,1,lo_in_CodePos - 1);
          par_te_String := DELSTR(par_te_String,1,lo_in_CodePos - 1 + STRLEN(par_te_Sign));

          lo_in_CodePos := STRPOS(par_te_String,par_te_Sign);
          i += 1;

          par_te_Array[i] := par_te_String;
          lo_bo_Finished := TRUE;

      UNTIL lo_bo_Finished;

Read ArcherPoint's Blog Follow us on Twitter Follow us on Facebook Follow us on LinkedIn Link to our RSS feed Join us on Google+ Watch us on YouTube