Here is a story from 25 years ago, when I decided to build a better NAV than NAV and decided to do it in a rather complicated way. Since we are still all using the Microsoft product, I clearly failed, but the result is still an interesting story. Check out the video:

In this holiday-season video, Erik takes us on a nostalgic journey back roughly 20 years to a time when he set out to build a better ERP system than Navision (NAV). What started as a frustration with VAT posting groups in Navision Financials v1.23/2.0 turned into a multi-year passion project involving a custom development environment, a Lua-based scripting engine, a database layer, and even a point-of-sale system. Erik demos the actual software — still compilable and runnable today — and reflects on the lessons learned from this ambitious undertaking.
The Catalyst: VAT Posting Groups
Erik traces the origin of this project back to a specific design decision in Navision Financials around version 1.23 or 2.0. At the time, the system had General Business Posting Groups and General Product Posting Groups. Then, for reasons Erik still considers a mistake, VAT Business Posting Groups and VAT Product Posting Groups were added as separate structures.
To this day, Erik says he has never encountered a customer who needed their sales and purchase VAT posting separated into a different structure in the General Ledger than their regular sales and purchase posting. It simply meant more fields to fill out. Microsoft has improved this over the years with defaulting mechanisms, but that original frustration was the catalyst for a much bigger idea: what if you could build a better NAV than NAV?
From Modula-2 to Lua
Around 1997, Erik began building a Modula-2 compiler. Modula-2 was the successor to Pascal (the language family that AL is based on), and it had many nice features. But then something changed his direction entirely.
Back in the era of computer magazines, Erik read an article in Byte Magazine about a peculiar programming language created by three developers in Brazil. The language was called Lua (meaning “moon” in Portuguese). Intrigued, Erik went to the FTP site listed in the article, downloaded the Lua source code over his dial-up modem, and loaded it into his Watcom C++ compiler.
The result was remarkable: the code compiled on the first try with zero warnings and zero errors. For anyone who lived through the era of incompatible C compiler implementations and poorly defined standards, this was nearly unheard of. Erik was so impressed by Lua’s quality that he abandoned his Modula-2 compiler project and decided to build his platform on Lua instead.
Introducing Ukor
The system was eventually named Ukor — a name born from the practical necessity of finding an available five-character .com domain in the late 1990s, leading to ukor.com.
Erik’s demo runs at very low resolution because the software was built in an era when “a pixel was a pixel” — before high-DPI screens existed. Running it on a modern display makes it appear tiny, but the functionality is all there.
The Bootstrapping Philosophy
One of the core ideas behind Ukor was bootstrapping — writing the system in itself. At the time, one of Erik’s frustrations with NAV was that it was something of a “last mile” system in terms of what you could customize. You couldn’t extend and modify it the way we can with Business Central today.
Erik wanted a system where the development environment itself was built using the system’s own tools. And that’s exactly what he achieved. The development suite — with its tables, forms, reports, layouts, source code editor, resources, templates, and even source code management (integrated with Microsoft SourceSafe) — was all written in Lua code running within Ukor.
Here’s an example of what that self-hosted code looked like — drawing the development window, defining menus, building the UI, and wiring up functionality — all in augmented Lua.
The Framework Layer
Beneath the applications sat a framework called uFrame, which provided database operations built on dBASE files. The API was heavily inspired by NAV’s C/AL conventions:
- Create a table based on a layout definition
- Open, verify, modify, delete records
- Compute fields
- Set and get filters
- Validate fields
- Work with captions
All of this was accessible in an object-oriented style — a significant investment of time, but it created a coherent application development platform.
The “Better NAV” Application
The financial application Erik built on this platform took a different philosophical approach, partly inspired by a system called Koda, where everything was simply an account.
Account Types as the Universal Concept
Instead of having separate, hard-coded entity types (customers, vendors, items, GL accounts), Ukor let you define account types with configurable properties:
- Primary type — like the General Ledger
- Non-primary types — functioning more like dimensions
- Direction — to define whether something relates to purchases or sales
- Shadow posting — so that posting an item transaction would also generate GL entries
- Must balance — equivalent to NAV’s consistency check
- Place on main menu — primary types would automatically appear in the dynamically built main menu
Each account type could have its own set of functions — chart of accounts views, journals, posting, filtering by dates and periods, ledger entries, and more. Since everything was an account, customers, items, and GL accounts were all handled through the same unified structure.
Documents and Offline Capability
The system supported document types (invoices, journals, credit memos, inventory journals) with configurable properties like auto-print and server-side posting. But perhaps the most forward-thinking feature was its offline capability.
In the late 1990s era of dial-up modems, Erik built a local/server document architecture. You could work offline, creating documents locally, and then when you reconnected to the server, documents would be transmitted and posted server-side. In the demo, Erik creates an invoice — selecting a customer, entering a date (typing “T” for today), adding 45 lunchboxes as line items — and then chooses “Post document on server,” which triggers the server console to process the transaction.
Auto-Layout for Fields
One design decision that Erik was particularly proud of addressed a pain point that still exists in Business Central today: when you add a field to a table, you also have to manually add it to pages. In Ukor, fields had properties indicating where they should appear — browsing, editing, printing — enabling auto-layout. A “tab break” property on a field would automatically start a new section in the UI. Erik demonstrates this live, toggling the tab break property on an address field and watching the form reorganize itself.
A Sample of NAV-Inspired Code
The source files from the project reveal just how deeply NAV-inspired the coding patterns were. Here’s a snippet from a “Product Group” post-processing routine — note the familiar constructs like dbSETRANGE, dbFINDREC, dbGETREC, dbTESTFIELD, and ERROR:
dbLOCKFILE(ProdGrEntry);
dbCALCFIELDS("Stock(CPrice)");
dbTESTFIELD("Stock(CPrice)",0);
dbSELECTKEY(ProdGrEntry."Prod.Gr No.");
dbSETRANGE(ProdGrEntry."Prod.Gr No.","No.");
dbMODIFYALL(ProdGrEntry."Prod.Gr No.",'');
dbSETRANGE(CommLine.File,6);
dbSETRANGE(CommLine."No.","No.");
dbDELALL(CommLine);
dbSELECTKEY(PurchaseLine."Voucher Type",PurchaseLine.Type,
PurchaseLine."No.",PurchaseLine."Deliv.Date");
dbSETRANGE(PurchaseLine."Voucher Type",1);
dbSETRANGE(PurchaseLine.Type,4);
dbSETRANGE(PurchaseLine."No.","No.");
IF dbFINDREC(PurchaseLine,'-') THEN
ERROR('There are purchase orders for product group %1',"No.");
dbSELECTKEY(SalesLine."Voucher Type",SalesLine.Type,
SalesLine."No.",SalesLine."Deliv.Date");
dbSETRANGE(SalesLine."Voucher Type",1);
dbSETRANGE(SalesLine.Type,4);
dbSETRANGE(SalesLine."No.","No.");
IF dbFINDREC(SalesLine,'-') THEN
ERROR('There are sales orders for product group %1',"No.");
The SAVE trigger simply stamps the current date:
"Edited on" := TODAY;
And field validation logic like the Name field’s search name generation shows sophisticated string manipulation:
IF (1 < CursorPos) AND (CursorPos < MAXSTRLEN("Search Name")) THEN BEGIN
"Search Name" := DELCHR(COPYSTR(Name,CursorPos),'<>');
"Search Name" := SETSTRLEN("Search Name" + ' ' +
DELCHR(COPYSTR(Name,1,CursorPos-1),'<>'),MAXSTRLEN("Search Name"));
END ELSE
"Search Name" := Name;
The “Superior Prod. Gr” field validation demonstrates record lookups and field inheritance — when a superior product group is specified, the booking group is inherited from it:
IF ("Superior Prod. Gr" <> '') THEN BEGIN
dbGETREC(ProdGr2,"Superior Prod. Gr");
"Booking Group" := ProdGr2."Booking Group";
END;
Anyone familiar with C/AL or AL will immediately recognize the patterns — this was effectively a parallel evolution of the same design philosophy.
VelociPOS: The One That Shipped
While the financial application never reached production readiness, one application built on the Ukor platform did achieve commercial success: a point-of-sale system called VelociPOS (named in the era right after Jurassic Park). It was a fairly advanced POS system for its time, featuring:
- Item management with customer prices
- Mix-and-match discount systems
- Payment terminal integration
- Direct transaction links to Navision
- Multi-level offline capability — the store could be offline from Navision, and individual cash registers could be offline from the store server, ensuring receipts could always be processed
VelociPOS was never known by the Ukor name — it had its own brand and attracted a fair number of customers.
Still Compiling After All These Years
One detail that genuinely surprised Erik: the entire C codebase compiled successfully with the latest version of Visual Studio, requiring only a couple of preprocessor defines. Everything works at least as well as it did 20 years ago.
Erik notes that people often criticize Windows for maintaining old, inconsistent code, but the amount of effort Microsoft has put into backward compatibility is actually remarkable. The fact that a 20+ year old Win32 application can compile and run on a modern system is a testament to that commitment.
That said, Erik looked at two random functions that morning and found both had potential memory leaks — a reminder of different coding standards from a different era. He’s leaving the code exactly as it was.
Lessons and Legacy
Erik is candid about the outcome: he failed in creating something that was a better NAV than NAV. Navision was actually pretty good — apart from that VAT Business Posting Group thing, which still annoys him.
But the project touched every aspect of building an application environment: compilers, interpreters, database engines, UI frameworks, development tools, source code management, financial applications, and commercial point-of-sale systems. Some of the ideas from Ukor found their way into later work, including the Simple Object Designer and other tools.
The takeaway Erik offers: if you want to build something, build it. You might not succeed at the original goal, but the experience and learning you gain along the way is invaluable. The project consumed a tremendous number of hours and produced code that was never meant for public consumption — but 20 years later, it still runs, and the lessons it taught shaped everything that came after.