Does old AL code rot?

What happens if you find the old code you wrote for BC version 16 and try using it with the latest version? In this video, I’m resurrecting some of the old code from when I started the YouTube channel. Check it out:

https://youtu.be/Dn_JrR4x0xI


In this video, Erik explores whether AL code written four years ago for Business Central still compiles and works today. The experiment is prompted by a real customer scenario: they need a lightweight point-of-sale solution, and Erik recalls building exactly that in a YouTube video back in August 2020. With a customer demo the very next morning, he pulls the old code out of the drawer to see if it still runs on Business Central version 24.

The Setup: A Customer Needs Point of Sale

The story begins with a customer who has a few retail outlets and needs some sort of point-of-sale capability alongside their Business Central implementation. There are full-featured POS solutions available that essentially take over the system, but for a customer doing only a handful of walk-in transactions per day, that’s overkill.

Erik mentions the concept of “point of sale light” — tweaking Business Central itself to be POS-friendly rather than bolting on an entirely separate system. And that’s when he remembers: he built exactly this kind of thing on YouTube back in 2020.

Pulling Code from the Drawer

Erik stores all the source code from his YouTube videos in a repository (linked from his videos). Symbols aren’t stored alongside the code, so the first step is opening the project and dealing with the inevitable red squiggles. The code originally targeted version 18 with runtime 8.0, though Erik notes he may have reused this app as a guinea pig for other demonstrations over the years.

After pointing the launch configuration at his sandbox and downloading symbols for version 24, the red disappears — the code compiles.

Fixing Warnings: Implicit “with” and Promoted Actions

While the code compiles cleanly, there are warnings. The most common: “use of implicit with will be removed in the future.” This is a well-known deprecation in AL where record variables were implicitly scoped using with blocks.

Fortunately, there’s a code action for this. Erik demonstrates using both Microsoft’s built-in code action (“Qualify with Rec — all occurrences in this object”) and code actions from other providers to quickly resolve these warnings across the codebase.

There’s also a warning about a Promoted action on a list part page. Since promoted actions can only be used on certain page types, and this is a lines subpage, the fix is simply removing the promotion. With that, the project compiles with zero warnings and zero errors.

Understanding “POS Light” in Business Central

Erik draws an important distinction between proper point-of-sale and what this solution does. A proper POS system — like Velocci, which Erik built years ago — handles cash register accounting, clerk switching, mix-and-match discount rules, and performance metrics like average scanning time per item per clerk.

This solution is something much simpler. The key insight for turning Business Central into a basic POS system is remarkably straightforward:

The One-Field POS Trick

The single most important field is the Bal. Account No. on a payment method. When you post an invoice with a payment method that has a balance account specified, Business Central automatically applies a payment entry. The posted invoice is immediately closed — it’s never left open as an outstanding receivable. The balancing entry goes to your cash account (or a credit card settlement account).

With just this configuration, anyone can:

  • Strip down the sales invoice page to show only essential fields (using personalization)
  • Set up a “Cash” payment method with a balancing account pointed at a cash G/L account
  • Set up a “Card” payment method with a balancing account pointed at a settlement account
  • Post invoices that are immediately paid — no open receivables

That’s the lowest bar for a POS-like experience: a simplified sales order entry with automatic payment application.

Testing the Barcode Scanner

Erik hooks up a physical barcode scanner (a keyboard-wedge type device) and tests the scanning workflow. The original concept behind this project was: what if a scanner is connected to the computer, and you need to ensure the scanned barcode is processed correctly rather than being typed into whatever field happens to have focus (like quantity or amount)?

The scanning works perfectly. Scanning an item adds it to the receipt. Scanning the same item again increments the quantity. Scanning a “Cash” barcode (a printed barcode on a piece of paper sitting on the counter) posts the invoice and resets for the next transaction. The idea is that you could operate the entire POS workflow using just a barcode reader — item barcodes for products, and printed action barcodes for payment methods.

Diving into the Code

Erik walks through the key parts of the scanning logic:

New Receipt

The NewReceipt function creates a sales header of type Sales Invoice, assigns it to the cash customer defined in setup, and sets the posting date to today.

Scan Item

When a barcode is scanned and the POS status is “Active,” the code checks if the scanned value matches a known item. In the ScanItem function:

  1. Check if the item is already on the current receipt
  2. If yes, increment the quantity
  3. If no, find the last line number, calculate the next line number, and insert a new sales line

Discovering Some Quirks

Not everything is perfectly smooth. Erik encounters a few issues:

The Zero Quantity Problem

Changing a line’s quantity to zero triggers an error: “The field Quantity on table Sales Line has changed in the database between initial and jetload.” This appears to be related to how the page handles record loading and refreshes — possibly a change in Business Central’s page infrastructure since the code was originally written. The workaround: just delete the line instead of setting quantity to zero.

The Total Section Not Updating

The totals section on the page doesn’t refresh automatically after scanning. Erik traces this back to an interesting design choice in the original code: after scanning, the code would release the sales document. This wasn’t necessarily for business logic reasons — it was to force a page refresh. When Erik removes the release call, totals stop updating.

He experiments with adding CurrPage.Update(false) as an alternative to the release/reopen cycle, and confirms this also works to trigger the refresh. This is likely a case where Microsoft optimized page refreshes over the years for performance, inadvertently affecting this workaround.

The Build Pipeline

The repository also includes a build configuration that Erik used with his ALBuild pipeline tool. The pipeline definition in pos.json shows a complete CI/CD workflow:

{
  "Project": "Point of Sale",
  "Report" : "Email",
  "ReportDestination" : "erik@hougaard.com",
  "Tasks": [
    {
      "Type": "DeployBasicDocker",
      "Settings": {
        "AppFile": "c:\\projects\\albuild\\testrunner\\Hougaard_ALBuild TestRunner_1.0.0.0.app",
        "BaseURL": "http://bc20:7049/BC/",
        "User": "demo",
        "Password": "demo",
        "SchemaUpdateMode": "forcesync"
      }
    },
    {
      "Type": "Git",
      "Settings": {
        "Path": "c:\\projects\\youtube\\point of sale",
        "Command": "pull"
      }
    },
    {
      "Type": "UpdateVersion",
      "Settings": {
        "AppPath": "c:\\projects\\youtube\\point of sale",
        "VersionPartToIncrement": 4,
        "Increment": 1,
        "DateInVersionPartNo": 3
      }
    },
    {
      "Type": "Compile",
      "Settings": {
        "AppPath": "%APPPATH%"
      }
    },
    {
      "Type": "Translate",
      "Settings": {
        "XLFPath": "%APPPATH%\\Translations\\%NAME%.g.xlf",
        "ProductName": "%NAME%"
      }
    },
    {
      "Type": "Compile",
      "Settings": {
        "AppPath": "%APPPATH%"
      }
    },
    {
      "Type": "Git",
      "Settings": {
        "Path": "%APPPATH%",
        "Command": "commit -a -m \"ALBuild Version %VERSION%\""
      }
    },
    {
      "Type": "Git",
      "Settings": {
        "Path": "%APPPATH%",
        "Command": "push"
      }
    }
  ]
}

The pipeline pulls from Git, increments the version number (with a date stamp), downloads symbols, compiles, handles translations, and pushes back to Git — a complete automated build cycle.

What About Printing Receipts?

One challenge Erik acknowledges but doesn’t solve in this video: receipt printing. In the cloud, you’d need to use Universal Print or another supported printing option to output to a thermal receipt printer. This is one of the practical considerations when building a POS-like experience on top of Business Central SaaS.

Conclusion

So does old AL code rot? Far less than you might expect. Code written for Business Central version 18 in 2020 compiled against version 24 symbols with only minor fixes needed:

  • Qualifying implicit with statements (handled by code actions)
  • Removing an invalid promoted action on a subpage
  • Updating the app.json for the current platform and runtime

The biggest issues Erik encountered weren’t really code rot at all — they were artifacts of him reusing the project as a testbed for other demonstrations over the years. The core scanning and POS logic still works as intended.

For the customer demo, this old code serves as an excellent conversation piece: it helps establish what the customer actually needs. Do they need a full-featured POS with clerk management and mix-and-match discounting? Can they get by with just a balance account on a payment method and a personalized page? Or is the answer somewhere in between — perhaps something like this lightweight scanning solution? That’s the real value of pulling code from the drawer.