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
- Create a table named
"Loyalty Member"with these fields:"Member No."of typeCode[20]— the primary keyDescriptionof typeText[100]"Enrolled On"of typeDate"Last Updated On"of typeDate
- Give the table an
OnInserttrigger that stamps"Enrolled On"withWorkDate()— unconditionally, even when the caller already filled the field: for a normal registration, the system is the authority on the enrollment date. - Give the table an
OnModifytrigger that stamps"Last Updated On"withWorkDate()— unconditionally. - 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")RegisterMemberinserts the record so that theOnInserttrigger runs — the enrollment stamp appears.MigrateMemberinserts the record so that theOnInserttrigger does not run — every imported field value, including"Enrolled On", lands in the database exactly as provided.UpdateMembermodifies the record so that theOnModifytrigger runs —"Last Updated On"is stamped.PatchMigratedMembermodifies the record so that theOnModifytrigger 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
- OnInsert (Table) trigger — when the trigger runs, and why code-based inserts only fire it on request.
- OnModify (Table) trigger — the modify-side counterpart.
- Record.Insert(Boolean) method — the RunTrigger parameter and its default.
- Record.Modify([Boolean]) method — same contract for modifying records.
- Table object — declaring fields, keys, and triggers on a new table.
Insert and Modify take an optional Boolean RunTrigger parameter that decides whether the table's OnInsert/OnModify code runs — and it defaults to false.OnInsert and OnModify triggers, unconditionally — the codeunit then only chooses whether those triggers run.RunTrigger as true; the migration and patch paths pass false, so imported audit dates reach the database untouched.