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.
I 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 EXIT; FOR i := 1 TO RecRef.FIELDCOUNT DO BEGIN FldRef := RecRef.FIELDINDEX(i); xFldRef := xRecRef.FIELDINDEX(i); IF IsNormalField(RecRef.NUMBER,FldRef.NUMBER) THEN IF FORMAT(FldRef.VALUE) <> FORMAT(xFldRef.VALUE) THEN IF IsLogActive(RecRef.NUMBER,FldRef.NUMBER,1) THEN InsertLogEntry(FldRef,xFldRef,RecRef,1); END;
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:
// COMPARE CUSTOMITEM WITH TEMPCUSTOMITEM TO SEE IF ANY CHANGES RecRef1.GETTABLE(CustomItem1); RecRef2.GETTABLE(TempCustomItem); RecordsMatch := CustomFunctions.CompareTwoRecords(RecRef1,RecRef2); IF RecordsMatch THEN CurrReport.SKIP ELSE BEGIN // UPDATE CUSTOMITEM RECORD FROM TEMPCUSTOMITEM CustomItem.TRANSFERFIELDS(TempCustomItem); CustomItemStatus := 'UPDATE'; CustomItem.MODIFY; END;
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 // TEST DATA IN FIELDS BEGIN THE SAME FOR I := 1 TO RecRef1.FIELDCOUNT DO BEGIN FldRef1 := RecRef1.FIELDINDEX(I); FldRef2 := RecRef2.FIELDINDEX(I); IF FldRef1.VALUE <> FldRef2.VALUE THEN EXIT(FALSE); // ENDS WHEN NON-MATCHED FIELD FOUND END; EXIT(TRUE); // RECORDS MATCH!
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.
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!