Rec, Meet OnRun
Sales wants every newly onboarded customer to leave the onboarding step with a welcome note on the card and, when nobody has chosen payment terms yet, the introductory WELCOME terms. The same logic is needed from two very different callers: an action on the Customer Card that simply runs a codeunit against the current record, and an import job that holds a codeunit variable and calls a procedure on it. Business Central has a documented shape for exactly that: a codeunit bound to a table through its TableNo property, whose OnRun trigger does nothing but hand its record to a public procedure.
Setting TableNo changes the signature of OnRun without you writing it: the trigger receives a var record of that table under the name Rec. When a caller runs the codeunit with a record — Codeunit.Run(Codeunit::"Customer Welcome", Customer) — that Rec is the caller's own variable, passed by reference, so every field the codeunit changes on Rec is visible to the caller the moment the call returns, with no database write involved. Run the codeunit with a record from any other table and the platform refuses at run time. The tests deliberately do not capture the return value of Codeunit.Run: if Codeunit.Run(...) then would commit the transaction, and grading runs inside one that is rolled back.
Requirements
- Create a table extension that extends the
Customertable with a field named"Welcome Note"of typeText[250]. The starter already declares it; keep the name exactly. - Create a codeunit named
"Customer Welcome"bound to theCustomertable through itsTableNoproperty, with one public procedure:
procedure Apply(var Customer: Record Customer)- The codeunit's
OnRuntrigger hands itsRectoApplyand does nothing else, so the two entry points —Codeunit.Run(Codeunit::"Customer Welcome", Customer)andWelcome.Apply(Customer)— behave identically.
Rules for Apply:
"Welcome Note"becomes the textWelcome aboard,followed by the customer'sNameand an exclamation mark — for a customer namedNorthwind Tradersthat is exactlyWelcome aboard, Northwind Traders!. It is written every time, whatever the field held before.- When
"Payment Terms Code"is blank, it becomesWELCOME. A customer who already has a payment terms code keeps it. The tests make sure a payment terms record with codeWELCOMEexists, so validating the field is as safe as assigning it. Applychanges only the record it was handed, in memory. It must not callModifyorInsert— whether and when the changes are saved is the caller's decision.
Pick object IDs in the range 50100–50199 and reference other objects by name, never by ID.
What the tests check
The grading tests create customers through the standard test library, set a known Name, blank out "Payment Terms Code" and "Welcome Note" on the stored row, sometimes pre-fill one of those fields on the record in memory, and then drive your codeunit through both entry points. Through Apply they check that the caller's record carries the exact note for a fixed name and for a generated one (mind the comma, the space and the exclamation mark), that a "Welcome Note" that already held other text is overwritten with the new note, that a blank "Payment Terms Code" becomes WELCOME while an existing code is kept, and that the customer's row in the database is still untouched afterwards — a Modify inside Apply fails that test. Through Codeunit.Run(Codeunit::"Customer Welcome", Customer), return value not captured, they check that the caller's own Customer variable shows the note and the payment terms afterwards, that an existing code is kept on this path too, and that the stored row is still untouched on this path as well — a Modify in OnRun fails that test just as one in Apply does; a codeunit whose OnRun works on a copy of Rec, or whose Apply takes the record by value, passes nothing back and fails here. One test reads the codeunit's TableNo from the "CodeUnit Metadata" table and expects the Customer table, one runs the codeunit with an Item record and expects the platform's own refusal, whose message contains is not compatible with Codeunit.Run, and one checks that "Welcome Note" is declared with a maximum length of exactly 250.
Learn More
- TableNo property — how the property gives
OnRunits implicitvar Recparameter. - Codeunit object — the official example of a codeunit usable both through
Codeunit.Runand through a procedure call. - Codeunit.Run(Integer [, var Record]) method — running a codeunit with a record, the run-time error for a record from another table, and why capturing the return value commits.
- OnRun (Codeunit) trigger — the trigger that runs when a codeunit is run.
TableNo property names the table it is run with. Once it is set, OnRun silently gains a var record parameter of that table called Rec — and a caller that passes a record from any other table is refused at run time.Rec inside OnRun is the caller's own variable, passed by reference. Hand it straight to Apply; copying it into a local record first, or taking the record by value in Apply, cuts the link and the caller sees none of the changes.Apply writes the note as the text Welcome aboard, , the customer's Name and !, and touches "Payment Terms Code" only when it is blank. Do not call Modify anywhere, OnRun included — the tests read the row back from the database after both entry points and expect it unchanged.