0120

When Your Try Isn't a Try

Error-handling
Medium
tryfunction
getlasterrortext
error-propagation

When Your Try Isn't a Try

The pack station scans package codes into a batch, and before the shipment can be released the validator has to answer one question: which codes are good, and what is wrong with the rest. Two things must never happen. A single mistyped code must not abort the whole batch — catching scan errors is the entire reason the validator exists. And a broken configuration must not be filed away as if it were just one more bad code: when the setup the validator reads is unusable, nothing about the batch can be judged, and whoever started the run has to hear about it.

The starter already sketches the loop. It does not behave: the first bad code takes the run down with it, and the fix has a second trap waiting behind it.

What you get

The starter ships a setup table named "Package Scan Setup" with the fields "Primary Key" (Code[10], the primary key) and "Code Length" (Integer). It is a singleton — the one record that counts has a blank "Primary Key". Keep the table unchanged and include it in your submission: the grading tests write it directly by these names.

It also ships the codeunit named "Package Scan Validator", with the three public procedures below and a batch loop that you have to make work.

Requirements

procedure ResolveCodeLength(): Integer
procedure CheckScan(ScannedCode: Text; CodeLength: Integer)
procedure ValidateBatch(ScannedCodes: List of [Text]; var Failures: List of [Text]): Integer

ResolveCodeLength

Reads the configuration and returns the required code length:

  1. No "Package Scan Setup" record with a blank "Primary Key" — raise Package Scan Setup is missing.
  2. "Code Length" zero or negative — raise Code Length must be greater than zero in Package Scan Setup.
  3. Otherwise return "Code Length".

CheckScan

Checks one scanned code against a required length. It raises the exact message of the first broken rule below; a good code returns silently.

  1. '%1' must be exactly %2 characters long. — when the code's length is not CodeLength. %1 is the scanned code, %2 is CodeLength. Note the single quotes around the code and the full stop.
  2. '%1' must contain digits only. — when any character of the code is anything other than 0–9. %1 is the scanned code.

ScannedCode is whatever the scanner produced: it may be empty, too long, or full of letters.

ValidateBatch

Returns how many codes passed, and reports the rest:

  • Clear whatever Failures already holds before anything else — a second run must not inherit the first run's findings.
  • A scan problem never reaches the caller. Each failing code adds exactly one entry to Failures — the exact message CheckScan raises for that code — and the codes after it are still checked. Failures follows the order of ScannedCodes. A batch in which every single code is broken still returns normally.
  • A configuration problem is the exact opposite. The error ResolveCodeLength raises travels out of ValidateBatch to the caller with its message intact, and never lands in Failures. That holds whatever the batch looks like: a batch of perfectly good codes, a batch of broken ones, and an empty batch alike.
  • With a usable configuration, an empty batch returns 0 and leaves Failures empty.

Pick object IDs in the range 50100–50199, and reference every object by name, never by ID. Captions and tooltips are good practice here but are not graded.

What the tests check

CheckScan is called directly with a generated code of the required length (it must return silently), with a code one character too short and one too long, with a code of the right length carrying a letter, with one carrying a non-digit symbol, and with a code that breaks both rules at once — the raised messages are compared character for character, quotes and full stop included. ResolveCodeLength is checked against a configured length, against an empty setup table, and against a "Code Length" of exactly 0 and a negative one. ValidateBatch then runs on a usable setup: one broken code must come back as a single Failures entry and a return value of 0 — the call is made directly, so an error raised there fails the test; a broken-good-broken-good batch must return 2 with both messages in scan order; an all-good batch of three returns 3 with an empty Failures; an empty batch returns 0; and a Failures list pre-filled with leftovers comes back empty. Finally, the setup is broken — the record deleted, or its "Code Length" set to 0 — and ValidateBatch is expected to fail: the tests catch the error themselves and compare it with the exact message ResolveCodeLength raises, including for a batch whose codes are all fine and for an empty batch, and Failures must come back empty in each of those runs — a configuration error is never filed as a per-code failure.

Learn More

Hint 1
The loop already routes the check through an error-catching wrapper, and the first broken code still takes the caller down. Nothing about the wrapper is wrong — the way the call itself is written is what decides whether anything gets caught.
Hint 2
A try call only catches when its implicit Boolean return value is used: if TryCheckScan(...) then ... else ... catches, while the bare statement TryCheckScan(...); is an ordinary call and the error passes straight through. Right after a catch, GetLastErrorText() holds the message that was raised.
Hint 3
Second trap, and it only appears once the first one is fixed: the arguments of a try function are evaluated inside it. With ResolveCodeLength() sitting in the argument list, a broken setup is caught by the very call that was meant to catch scan problems, and it becomes a per-code failure instead of reaching the caller. Resolve the length into a local variable before the loop and pass that variable.
ALBusiness Central 28.4
Press Compile to check your code compiles — Submit runs the tests.
The code editor is desktop-only
Open this problem on a computer to write and run code. Reading the description, tests and discussion works fine here.