0036

Two Insert Paths: The RunTrigger Contract

Basics
Easy
table
triggers
runtrigger
migration

Two Insert Paths: The RunTrigger Contract

Your company is replacing a legacy loyalty system. New members sign up every day and must be stamped with today's enrollment date, while thousands of legacy members arrive through a migration job and must keep the audit dates they earned years ago. Same table, two write paths — and the only difference between them is whether the table's triggers run.

Requirements

  1. Create a table named "Loyalty Member" with these fields:
    • "Member No." of type Code[20] — the primary key
    • Description of type Text[100]
    • "Enrolled On" of type Date
    • "Last Updated On" of type Date
  2. Give the table an OnInsert trigger that stamps "Enrolled On" with WorkDate() — unconditionally, even when the caller already filled the field: for a normal registration, the system is the authority on the enrollment date.
  3. Give the table an OnModify trigger that stamps "Last Updated On" with WorkDate() — unconditionally.
  4. Create a codeunit named "Member Registration" with four procedures:
procedure RegisterMember(var Member: Record "Loyalty Member")
procedure MigrateMember(var Member: Record "Loyalty Member")
procedure UpdateMember(var Member: Record "Loyalty Member")
procedure PatchMigratedMember(var Member: Record "Loyalty Member")
  1. RegisterMember inserts the record so that the OnInsert trigger runs — the enrollment stamp appears.
  2. MigrateMember inserts the record so that the OnInsert trigger does not run — every imported field value, including "Enrolled On", lands in the database exactly as provided.
  3. UpdateMember modifies the record so that the OnModify trigger runs — "Last Updated On" is stamped.
  4. PatchMigratedMember modifies the record so that the OnModify trigger does not run — a data-fix pass over migrated rows must keep the imported "Last Updated On".

Which path a caller gets is decided by the RunTrigger parameter of Insert and Modify — and its default is false, which is why a bare parameterless call is a classic source of "my trigger never fired" bugs. Write the parameter explicitly on every Insert and Modify call so the choice is visible in the code (LinterCop rule LC0040 flags calls that omit it); the explicitness itself is not graded — the behavior above is.

Pick object IDs in the range 50100–50199 and reference other objects by name, never by ID.

What the tests check

The grading tests drive members through your codeunit and read them back from the database: the register and update paths must come back with WorkDate() in the stamped field (even when the test pre-filled it); the migration paths must come back with the imported dates unchanged and the other changed fields saved. They also insert and modify a record directly with triggers running to verify that the stamping lives in the table's own triggers, not in the codeunit, and they check the declared maximum lengths of "Member No." (20) and Description (100).

Learn More

Hint 1
Insert and Modify take an optional Boolean RunTrigger parameter that decides whether the table's OnInsert/OnModify code runs — and it defaults to false.
Hint 2
Stamp the dates inside the table's own OnInsert and OnModify triggers, unconditionally — the codeunit then only chooses whether those triggers run.
Hint 3
The register and update paths pass RunTrigger as true; the migration and patch paths pass false, so imported audit dates reach the database untouched.
ALBusiness Central 28.5
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.