diff --git a/non production apps/EscapeRoomApp/.github/copilot-instructions.md b/non production apps/EscapeRoomApp/.github/copilot-instructions.md new file mode 100644 index 00000000..4a47d818 --- /dev/null +++ b/non production apps/EscapeRoomApp/.github/copilot-instructions.md @@ -0,0 +1,473 @@ +# Copilot Instructions: Creating Escape Room Venues on BCTalent.EscapeRoom + +## What Is This? + +The **BCTalent.EscapeRoom** framework (ID range 73920-73999) enables developers to create gamified, task-validated learning experiences inside Business Central. You build a **venue app** — a separate AL extension — that depends on the framework and adds venues, rooms, and tasks using AL interfaces. + +This file is the complete guide for an agent creating a new escape room venue from scratch. See [Docs/Framework/](Docs/Framework/) for detailed reference. + +--- + +## Part 1: Project Setup + +### app.json + +```json +{ + "id": "your-guid-here", + "name": "My Escape Room Venue", + "publisher": "Your Name", + "version": "1.0.0.0", + "dependencies": [ + { + "id": "f03c0f0c-d887-4279-b226-dea59737ecf8", + "name": "BCTalent.EscapeRoom", + "publisher": "waldo & AJ", + "version": "1.3.0.0" + } + ], + "idRanges": [{ "from": 50000, "to": 50099 }] +} +``` + +Always use the **vjeko-al-objid** tool (`getNextObjectId`) before creating any AL object to reserve a unique object ID. Never hardcode or guess IDs. + +### Recommended Folder Structure + +``` +MyVenueApp/ +├── Venue/ +│ ├── MyVenue.Codeunit.al (implements iEscapeRoomVenue) +│ └── EscapeRoomVenueExt.EnumExt.al +├── Rooms/ +│ ├── Room1MyRoom.Codeunit.al (implements iEscapeRoom) +│ └── EscapeRoomExt.EnumExt.al +├── Tasks/ +│ ├── R1T1MyTask.Codeunit.al (implements iEscapeRoomTask) +│ └── EscapeRoomTaskExt.EnumExt.al +├── Resources/ +│ ├── Room1MyRoomDescription.html +│ ├── Room1MyRoomSolution.html +│ └── RoomCompletedImage.png +└── Install/ + └── InstallVenue.Codeunit.al (registers venue via UpdateVenue) +``` + +--- + +## Part 2: Extending the Enums + +Three enums must be extended — one for each level of the hierarchy. + +```al +enumextension 50000 "My Venue Enum Ext" extends "Escape Room Venue" +{ + value(50000; MyVenue) { Caption = 'My Learning Venue'; } +} + +enumextension 50001 "My Rooms Enum Ext" extends "Escape Room" +{ + value(50001; Room1Introduction) { Caption = 'Room 1: Introduction'; } + value(50002; Room2Challenge) { Caption = 'Room 2: Challenge'; } +} + +enumextension 50002 "My Tasks Enum Ext" extends "Escape Room Task" +{ + value(50010; R1Task1CreateField) { Caption = 'Create Custom Field'; } + value(50011; R1Task2AddLogic) { Caption = 'Add Business Logic'; } + value(50020; R2Task1OptimizeQuery) { Caption = 'Optimize Query'; } +} +``` + +--- + +## Part 3: Implementing the Interfaces + +### 3.1 iEscapeRoomVenue + +```al +codeunit 50000 "My Venue" implements iEscapeRoomVenue +{ + procedure GetVenueRec() VenueRec: Record "Escape Room Venue" + var + Me: ModuleInfo; + begin + NavApp.GetCurrentModuleInfo(Me); + VenueRec.Id := Me.Name; + VenueRec.Name := Me.Name; + VenueRec.Description := 'One-line description of what participants will do'; + VenueRec.Venue := Enum::"Escape Room Venue"::MyVenue; + VenueRec."App ID" := Me.Id; + VenueRec.Publisher := Me.Publisher; + end; + + procedure GetVenue(): Enum "Escape Room Venue" + begin + exit(Enum::"Escape Room Venue"::MyVenue); + end; + + procedure GetRooms() Rooms: List of [Interface iEscapeRoom] + var + Room1: Codeunit "Room1 Introduction"; + Room2: Codeunit "Room2 Challenge"; + begin + Rooms.Add(Room1); + Rooms.Add(Room2); + end; + + procedure GetRoomCompletedImage() InStr: InStream + begin + // Return empty stream or load an image from resources + end; + + procedure GetVenueCompletedImage() InStr: InStream + begin + // Return empty stream or load a completion badge + end; +} +``` + +### 3.2 iEscapeRoom + +```al +codeunit 50010 "Room1 Introduction" implements iEscapeRoom +{ + procedure GetRoomRec() RoomRec: Record "Escape Room" + var + Me: ModuleInfo; + begin + NavApp.GetCurrentModuleInfo(Me); + RoomRec."Venue Id" := Me.Name; + RoomRec.Name := Format(this.GetRoom()); + RoomRec.Description := 'Short plain-text description for quick preview'; + RoomRec.Sequence := 1; + end; + + procedure GetRoom(): Enum "Escape Room" + begin + exit(Enum::"Escape Room"::Room1Introduction); + end; + + procedure GetTasks() Tasks: List of [Interface iEscapeRoomTask] + var + Task1: Codeunit "R1T1 Create Custom Field"; + Task2: Codeunit "R1T2 Add Business Logic"; + begin + Tasks.Add(Task1); + Tasks.Add(Task2); + end; + + procedure GetRoomDescription() Description: Text + begin + // Return HTML content string, or leave empty and load via BLOB + // See Resources/Room1IntroductionDescription.html + end; + + procedure GetRoomSolution() Solution: Text + begin + // Return HTML content string + // See Resources/Room1IntroductionSolution.html + end; + + procedure GetHintImage() InStr: InStream + begin + // Optional — return empty stream if no image + end; +} +``` + +### 3.3 iEscapeRoomTask + +```al +codeunit 50020 "R1T1 Create Custom Field" implements iEscapeRoomTask +{ + SingleInstance = true; // Required when using event subscribers + + var + Room: Codeunit "Room1 Introduction"; + + procedure GetTaskRec() TaskRec: Record "Escape Room Task" + var + Me: ModuleInfo; + begin + NavApp.GetCurrentModuleInfo(Me); + TaskRec."Venue Id" := Me.Name; + TaskRec."Room Name" := Format(Room.GetRoom()); + TaskRec.Name := Format(this.GetTask()); + TaskRec.Description := 'Add an "Information" field to the Customer table.'; + end; + + procedure GetTask(): Enum "Escape Room Task" + begin + exit(Enum::"Escape Room Task"::R1Task1CreateField); + end; + + procedure IsValid(): Boolean + var + Field: Record Field; + begin + Field.SetRange(TableNo, Database::Customer); + Field.SetRange(FieldName, 'Information'); + exit(not Field.IsEmpty()); + end; + + procedure GetHint(): Text + begin + exit('Add a text field named "Information" to the Customer table using a table extension.'); + end; +} +``` + +--- + +## Part 4: Registering the Venue + +The venue must be registered on install (and upgrade). The framework creates all records automatically from the interface implementations. + +```al +codeunit 50001 "Install My Venue" +{ + Subtype = Install; + + trigger OnInstallAppPerCompany() + begin + RegisterVenue(); + end; + + local procedure RegisterVenue() + var + EscapeRoom: Codeunit "Escape Room"; + MyVenue: Codeunit "My Venue"; + begin + EscapeRoom.UpdateVenue(MyVenue); + end; +} +``` + +The `UpdateVenue()` call walks the interface hierarchy (`GetRooms()` → `GetTasks()`) and creates or updates all records in the framework tables. + +--- + +## Part 5: Task Validation Patterns + +See [Docs/Framework/Task-Validation.md](Docs/Framework/Task-Validation.md) for full examples. Summary of the three patterns: + +### Pattern 1: Polling + +`IsValid()` returns `true` when the condition is met. Framework calls it periodically via `UpdateStatus()`. + +**Use for:** Field existence, object existence, configuration checks, app installation. + +```al +procedure IsValid(): Boolean +var + Field: Record Field; +begin + Field.SetRange(TableNo, Database::Customer); + Field.SetRange(FieldName, 'Information'); + exit(not Field.IsEmpty()); +end; +``` + +### Pattern 2: Event Subscriber + +`IsValid()` returns `false`. A separate event subscriber detects when the participant performs the right action and sets a flag. + +**Use for:** Monitoring table inserts/modifies, detecting specific user interactions, tracking data flow. + +**Critical requirements:** +- Codeunit must have `SingleInstance = true` +- Use `OnAfterInsertEvent` for new records (NOT `OnAfterModifyEvent` — inserts and modifies are distinct events) +- Store state in a codeunit-level variable + +```al +codeunit 50021 "R1T2 Add Business Logic" implements iEscapeRoomTask +{ + SingleInstance = true; + + var + TaskCompleted: Boolean; + + procedure IsValid(): Boolean + begin + exit(TaskCompleted); + end; + + [EventSubscriber(ObjectType::Table, Database::"Sales Header", OnAfterInsertEvent, '', false, false)] + local procedure OnSalesHeaderInserted(var Rec: Record "Sales Header"; RunTrigger: Boolean) + begin + if Rec."Document Type" <> Rec."Document Type"::Order then + exit; + TaskCompleted := true; + end; + // ... (GetTaskRec, GetTask, GetHint as usual) +} +``` + +### Pattern 3: Test Codeunit + +Framework runs a test codeunit to validate complex UI or workflow scenarios. + +```al +procedure GetTaskRec() TaskRec: Record "Escape Room Task" +begin + // ... + TaskRec.TestCodeunitId := Codeunit::"My Test Validation"; // Framework runs this +end; +``` + +--- + +## Part 6: Room and Task Design + +### Task Selection: Only Observable Problems + +Only include a task if the performance problem (or challenge) is **viscerally observable** — participants must be able to feel or clearly see the difference before and after. + +- Exclude tasks that only matter at extreme scale not present in the demo environment +- Exclude tasks that are "interesting in theory" but produce no noticeable effect in practice +- A locking/blocking scenario requires **actual concurrent blocking** between two live sessions — a slow sequential query is NOT a locking demonstration + +### For Performance Rooms: Key Rules + +See [Docs/Framework/Performance-Room-Design.md](Docs/Framework/Performance-Room-Design.md) for the full guide. The essential rules: + +1. **Minimum data:** At least 25,000 records are needed for performance differences to be perceptible in a group setting. + +2. **NST caching:** Add `SelectLatestVersion()` to every page action that triggers a demo measurement, with this exact comment: `// DO NOT REMOVE — needed for consistent demo results`. Without it, the BC NST cache makes the second run appear artificially fast. + +3. **Measurement code naming:** Use `R[room]-[SHORT-DESCRIPTION]` (e.g., `R4-ACTIVE`, `R7-N+1`). This code appears in **two places** — the page action that creates the measurement record, and the task's `IsValid()` that reads it. **They must match exactly.** A mismatch means the task can never be completed. + +4. **Use `OnAfterInsertEvent`** when subscribing to Performance Measurement records — they are inserted, never modified. + +5. **Compare vs. previous measurement**, not an absolute threshold. Require improvement in both duration AND SQL statement count. + +6. **Two-extension architecture (code-based venues only):** When participants need to modify AL code, provide a companion code app (e.g., a PTE). Ideally that app depends only on the framework. If it also needs infrastructure from the venue app (e.g., measurement tables or manager codeunits), that dependency is acceptable provided `"dependencyPublishingOption": "Ignore"` is set in the companion app's `launch.json`. Data/configuration venues (e.g., a consultant track where participants work only in BC UI) have no companion app at all. + +7. **Add `"dependencyPublishingOption": "Ignore"` to the companion app's `launch.json`** (code-based venues only) to prevent confusing errors when participants publish from VS Code. + +### Selecting the Best Anti-Pattern Example + +When choosing an example, rank by: (1) clarity of fix — small, targeted code change; (2) educational commonness — genuinely seen in production code; (3) measurability — dramatic, unambiguous improvement in SQL count and duration. + +The canonical N+1 example: **`CalcFields` inside a `repeat...until` loop → `SetAutoCalcFields` before `FindSet`**. It meets all three criteria. + +--- + +## Part 7: HTML Room Content + +Every room has a Description HTML file (what participants receive) and a Solution HTML file (what the facilitator and the post-delay view show). See [Docs/Framework/Room-Content-HTML.md](Docs/Framework/Room-Content-HTML.md) for the complete guide. The essential rules follow. + +### The One Rule That Overrides All Others + +**Descriptions present the MYSTERY. Solutions reveal the ANSWER.** + +The self-test: "Could they copy-paste something from this description to solve the task?" If yes, it is too revealing. + +### BC HTML Viewer Constraints + +No JavaScript, no external CSS, no emoji or emoticons (the BC HTML viewer cannot render them). Use inline styles only. + +### Description File Structure (7 sections, in this order) + +1. **HTML shell + H1** — `Room X: Short Title` +2. **TL;DR** — 2-3 sentences max. What's broken, what they'll do, how they prove success. +3. **The Challenge** — ONE paragraph (2-4 sentences). Sets scene + one-sentence "why this matters" woven in. No learning objectives. +4. **Your Mission** — H3 headings (descriptive names, never "Task 1:"). Each mission item has inline: goal, object/procedure reference, how-to-trigger steps (numbered `
    ` for BC UI only), `Hint:`, and `` validation note. +5. **Warning box** — Optional. Only for critical setup gotchas. +6. **Update Status reminder** — Required, just before What's Next. +7. **What's Next** — One sentence about the next room only. Omit for the final room. + +**Hints must be VAGUE about technique and PRECISE about location.** Use the exact BC action label as it appears in the UI. For non-toolbar actions, include the full navigation path: `Actions → Analytics & Reporting → Customer Order Analytics`. + +### Description Tone + +| Do | Do Not | +|---|---| +| "Operations are slow" | "Record-by-record loops cause slowness" | +| "We test your ability to..." | "You will learn about..." | +| "Find a better approach" | "Use SetLoadFields to fix this" | +| "Research what AL offers" | "Use DataTransfer for this scenario" | + +### Sections Forbidden in Descriptions + +`Skills Tested / Learning Objectives` — `Key Concepts / Research Topics` — `Business Impact` — `Performance Results / Baseline tables` — `Profiler Comparisons` — `How Task Validation Works` — `"You will learn"` language — Task numbers in headings — Emoticons / emojis — Standalone `Where to Look` / `Validation` / `What's Next listing all rooms` sections. + +### Solution File Structure + +1. **H1:** `Solution: Room X - Title` +2. **Introduction** (optional brief paragraph) +3. **Task sections** with descriptive H2 names (not "Task 1:"), step-by-step `
      ` instructions, complete `
      ` code blocks, and `

      Why This Matters:

      ` explanations +4. **Forbidden in solutions:** Performance Results comparison tables, Profiler Comparisons sections, task number references in headings +5. **Verification** checklist with Update Status reminder +6. **What You've Accomplished** summary + +### File Naming + +- Description: `Room[N][Topic]Description.html` — e.g., `Room2DataTransferDescription.html` +- Solution: `Room[N][Topic]Solution.html` — e.g., `Room2DataTransferSolution.html` + +--- + +## Part 8: Quality Checklists + +### AL Code Checklist + +- [ ] All object IDs reserved via vjeko-al-objid before creating objects +- [ ] Three enum extensions created (Venue, Room, Task) +- [ ] `GetVenueRec()` populates Id, Name, Description, Venue, App ID, Publisher +- [ ] `GetRoomRec()` populates Venue Id, Name, Description, Sequence +- [ ] `GetTaskRec()` populates Venue Id, Room Name, Name, Description +- [ ] Install codeunit calls `EscapeRoom.UpdateVenue()` +- [ ] Task codeunits with event subscribers have `SingleInstance = true` +- [ ] Event subscribers use `OnAfterInsertEvent` (not Modify) where inserting records +- [ ] Measurement codes match exactly between page action writer and `IsValid()` reader +- [ ] `IsValid()` compares vs. previous measurement (not absolute threshold) +- [ ] Both duration AND SQL count required for performance task completion +- [ ] `SelectLatestVersion()` added to all performance demo page actions +- [ ] `// DO NOT REMOVE` comment on `SelectLatestVersion()` calls +- [ ] Companion code app (if any) does NOT depend on venue app (one-way dependency only) +- [ ] `"dependencyPublishingOption": "Ignore"` in companion app's `launch.json` (if applicable) + +### HTML Description Checklist + +- [ ] Complete HTML structure with DOCTYPE +- [ ] TL;DR (2-3 sentences max) +- [ ] The Challenge (ONE paragraph, "why this matters" woven in as one sentence) +- [ ] Your Mission with descriptive H3 headings (no task numbers) +- [ ] Each mission item is self-contained: goal + location + hint + validation inline +- [ ] Hints use the exact BC UI action label; non-toolbar actions include navigation path +- [ ] No standalone Skills Tested, Key Concepts, Validation, or Where to Look sections +- [ ] No Performance Results or Baseline tables +- [ ] Update Status reminder present +- [ ] What's Next mentions only the next room (one sentence) +- [ ] Problem described ONCE (no repetition across sections) +- [ ] MYSTERIOUS — no method names, no solution code, no technical terms that reveal the answer +- [ ] No "you will learn" language +- [ ] No emoticons or emojis + +### HTML Solution Checklist + +- [ ] H1 with "Solution: Room X - Title" +- [ ] Descriptive H2/H3 headings (no task numbers) +- [ ] Complete, copy-pasteable code in `
      ` blocks
      +- [ ] "Why This Matters" after each major code block
      +- [ ] No Performance Results comparison tables
      +- [ ] No Profiler Comparisons sections
      +- [ ] Verification checklist with Update Status reminder
      +- [ ] "What You've Accomplished" summary
      +- [ ] No emoticons or emojis
      +
      +---
      +
      +## Reference
      +
      +| Doc | Contents |
      +|---|---|
      +| [Architecture.md](Docs/Framework/Architecture.md) | Core components, design patterns, status management |
      +| [Creating-Rooms.md](Docs/Framework/Creating-Rooms.md) | Full step-by-step AL walkthrough |
      +| [Task-Validation.md](Docs/Framework/Task-Validation.md) | Complete examples for all three validation patterns |
      +| [Room-Content-HTML.md](Docs/Framework/Room-Content-HTML.md) | Full HTML content guide with all rules and examples |
      +| [Performance-Room-Design.md](Docs/Framework/Performance-Room-Design.md) | Performance-themed room patterns: NST caching, measurement codes, task selection |
      +| [Telemetry-Integration.md](Docs/Framework/Telemetry-Integration.md) | Application Insights events and scoring |
      +| [API-Reference.md](Docs/Dev/API-Reference.md) | Complete interface method specifications |
      diff --git a/non production apps/EscapeRoomApp/Docs/Framework/Performance-Room-Design.md b/non production apps/EscapeRoomApp/Docs/Framework/Performance-Room-Design.md
      new file mode 100644
      index 00000000..46f6dadb
      --- /dev/null
      +++ b/non production apps/EscapeRoomApp/Docs/Framework/Performance-Room-Design.md	
      @@ -0,0 +1,274 @@
      +# Performance-Themed Room Design
      +
      +## Overview
      +
      +This guide covers patterns and rules specific to escape rooms where participants must diagnose and fix **performance problems** — slow queries, N+1 patterns, locking, or suboptimal AL code.
      +
      +Performance rooms can be structured in two ways:
      +
      +### Code-Based Venues (Two-Extension Architecture)
      +
      +Used when participants must **write or modify AL code** to fix the challenge:
      +
      +- **Venue app** (e.g., `OptimAL.EscapeRoom1`) — the framework implementation, rooms, tasks, HTML content, and measurement logic
      +- **Companion code app** (e.g., `OptimAL.PTE`) — the "broken" extension participants download, fix, and republish
      +
      +This is the right pattern when the fix requires editing AL source code (codeunit procedures, table extensions, etc.).
      +
      +### Data/Configuration Venues (Single-Extension Architecture)
      +
      +Used when participants are **Business Central users or consultants** working entirely in the BC UI — configuring settings, creating records, running reports, or analysing data. There is **no companion code app**. Task validation uses the Polling pattern (checking for records, field values, or configuration) or the Event Subscriber pattern (monitoring table inserts/modifies).
      +
      +Example: A consultant-track venue that tests knowledge of BC functional areas. Participants set up data, run processes, or configure modules — all inside BC. The venue app is the only extension.
      +
      +The rest of this document covers patterns that apply to **both** architectures, with the two-extension-specific rules called out explicitly.
      +
      +---
      +
      +## Two-Extension Architecture (Code-Based Venues Only)
      +
      +> This section applies only when participants need to modify and republish AL code. Skip this for data/configuration venues.
      +
      +### Dependency Direction
      +
      +The ideal dependency graph is strictly one-way:
      +
      +```
      +OptimAL.EscapeRoom1  ──depends on──►  BCTalent.EscapeRoom (framework)
      +OptimAL.PTE          ──depends on──►  BCTalent.EscapeRoom (framework)
      +```
      +
      +The companion app ideally depends only on the framework. However, if it also needs infrastructure from the venue app (for example, a performance measurement table or manager codeunit that lives in the venue app), a dependency on the venue app is acceptable — provided `"dependencyPublishingOption": "Ignore"` is set in the companion app's `launch.json` (see below).
      +
      +**Why this matters:** Without `"dependencyPublishingOption": "Ignore"`, publishing from VS Code temporarily uninstalls all dependency apps including the venue app, causing "could not be loaded" errors. The `Ignore` setting prevents this.
      +
      +### Publishing from VS Code
      +
      +Participants publish the companion app from VS Code using F5. Add this to its `launch.json`:
      +
      +```json
      +{
      +    "dependencyPublishingOption": "Ignore"
      +}
      +```
      +
      +Without this, the dependent venue app gets briefly uninstalled during the companion app's publishing cycle, causing confusing errors.
      +
      +---
      +
      +## Task Selection: Only Observable Problems
      +
      +A performance task earns its place only if attendees can **physically feel the slowness** with the current dataset.
      +
      +**Include a task if:**
      +- The operation takes several seconds with the test dataset
      +- Participants can trigger it easily from the UI and compare before/after themselves
      +- The improvement after fixing is dramatic and unambiguous (e.g., 30 seconds → 1 second)
      +
      +**Do NOT include a task if:**
      +- The problem only shows at extreme scale (>1M records) not present in the demo environment
      +- The performance difference requires instrumentation to notice
      +- The scenario is "interesting in theory" but produces no observable difference in practice
      +- The locking scenario requires timing that is impossible to reproduce consistently
      +
      +### Locking / Blocking Scenarios
      +
      +A "blocking" or "locking" demo requires **actual concurrent blocking** between two live sessions:
      +
      +- Two browser tabs logged in as different users
      +- One tab runs a long-running write (without committing)
      +- The other tab is blocked attempting to read or write the same records
      +
      +A **slow sequential query** does not demonstrate locking. Do not conflate the two — participants will be confused. If you cannot demonstrate actual blocking reliably in a workshop, do not include the task.
      +
      +---
      +
      +## Minimum Data Requirements
      +
      +At least **25,000 records** are needed for performance problems to register as meaningfully slow in a group workshop setting. Smaller datasets may produce imperceptible differences.
      +
      +Include a **"Generate More Test Data"** action in the PTE app or venue app. If the environment has fewer than 25,000 records, the description Warning box must instruct participants to run this first.
      +
      +---
      +
      +## Measuring Performance: The Measurement Pattern
      +
      +Performance rooms use a custom measurement table in the venue app to record operation metrics. The key types are duration and SQL statement count.
      +
      +### Measurement Code Naming Convention
      +
      +Format: `R[room]-[SHORT-DESCRIPTION]`
      +
      +Examples:
      +- `R4-ACTIVE` — Room 4, Active Customer Report
      +- `R7-N+1` — Room 7, N+1 query operation
      +- `R2-TRANSFER` — Room 2, table transfer operation
      +
      +The measurement code is the **primary key** that connects:
      +1. The page action that **creates** the measurement record
      +2. The task codeunit's `IsValid()` that **reads** the measurement record
      +
      +**These two strings must match exactly.** A mismatch means the task can never be completed — participants will see a task stuck permanently on "Open" with no error message.
      +
      +**Verification step after implementing any task:** Search the codebase for both occurrences of the measurement code string and confirm they are identical.
      +
      +---
      +
      +## NST Caching: The SelectLatestVersion Rule
      +
      +The BC NST (NAV Service Tier) caches data between requests. On the **second run** of a performance measurement, the NST cache can make the operation appear dramatically faster than it actually is. This destroys the demo — participants will think they have "fixed" the problem when they have not.
      +
      +**Fix:** Add `SelectLatestVersion()` to every page action that triggers a performance measurement demo:
      +
      +```al
      +action(RunActiveCustomerReport)
      +{
      +    ApplicationArea = All;
      +    Caption = 'Run Active Customer Report';
      +
      +    trigger OnAction()
      +    begin
      +        // DO NOT REMOVE — SelectLatestVersion needed for consistent demo results
      +        // Without it, NST caching makes the second run appear artificially fast.
      +        CurrPage.SetSelectionFilter(Rec);
      +        Rec.SelectLatestVersion();
      +
      +        PerformanceMeasurementMgr.StartMeasurement('R4-ACTIVE');
      +        RunActiveCustomerReport();
      +        PerformanceMeasurementMgr.StopMeasurement('R4-ACTIVE');
      +    end;
      +}
      +```
      +
      +Add the comment `// DO NOT REMOVE — ... needed for demo purposes` so that future developers do not clean it up.
      +
      +---
      +
      +## Task Validation for Performance Rooms
      +
      +Performance rooms use `IsValid()` on a task codeunit to decide whether a task is complete. The two common approaches are polling the measurement table directly or subscribing to table events.
      +
      +### Which Event to Subscribe To
      +
      +The Performance Measurement pattern has two phases:
      +
      +- `StartMeasurement()` **inserts** a new record (the "start" entry).
      +- `StopMeasurement()` **modifies** that same record to write Duration, SQL count, and other metrics.
      +
      +Choose the event based on what you want to detect:
      +
      +| Goal | Event |
      +|---|---|
      +| Detect that the participant ran the operation at all | `OnAfterInsertEvent` — fires when `StartMeasurement()` inserts the record |
      +| Validate the actual performance metrics (duration, SQL count) | `OnAfterModifyEvent` — fires when `StopMeasurement()` writes the results |
      +
      +Example — subscribe to metrics written by `StopMeasurement()`:
      +
      +```al
      +[EventSubscriber(ObjectType::Table, Database::"Performance Measurement", OnAfterModifyEvent, '', false, false)]
      +local procedure OnMeasurementCompleted(var Rec: Record "Performance Measurement")
      +begin
      +    if Rec.Code <> 'R4-ACTIVE' then
      +        exit;
      +    if Rec.SqlStatementsCount < 10 then
      +        TaskCompleted := true;
      +end;
      +```
      +
      +Example — subscribe to record existence inserted by `StartMeasurement()`:
      +
      +```al
      +[EventSubscriber(ObjectType::Table, Database::"Performance Measurement", OnAfterInsertEvent, '', false, false)]
      +local procedure OnMeasurementStarted(var Rec: Record "Performance Measurement")
      +begin
      +    if Rec.Code <> 'R1-BASELINE-UPGRADE' then
      +        exit;
      +    TaskCompleted := true;  // Participant triggered the operation
      +end;
      +```
      +
      +### Validation Approach
      +
      +Two approaches work well for validating performance improvements:
      +
      +**Absolute threshold** — require the metric to fall below a fixed value (e.g., SQL count < 10). Simpler, deterministic, and works without a prior baseline. Better for workshop settings where participants may not have a baseline measurement recorded.
      +
      +**Relative comparison** — compare the latest measurement against the previous measurement for the same code. Requires the participant to run the operation twice. More flexible but fails if no baseline exists.
      +
      +Example of absolute threshold (simpler for workshops):
      +
      +```al
      +procedure IsValid(): Boolean
      +var
      +    Measurement: Record "Performance Measurement";
      +begin
      +    Measurement.SetRange(Code, 'R4-ACTIVE');
      +    Measurement.SetCurrentKey(EntryNo);
      +    if not Measurement.FindLast() then
      +        exit(false);
      +    exit(Measurement.SqlStatementsCount < 10);
      +end;
      +```
      +
      +Example of relative comparison (if a baseline is always present):
      +
      +```al
      +local procedure ValidateCurrentMeasurement(NewMeasurement: Record "Performance Measurement"): Boolean
      +var
      +    PreviousMeasurement: Record "Performance Measurement";
      +begin
      +    PreviousMeasurement.SetRange(Code, NewMeasurement.Code);
      +    PreviousMeasurement.SetFilter(EntryNo, '<%1', NewMeasurement.EntryNo);
      +    if not PreviousMeasurement.FindLast() then
      +        exit(false);  // No baseline yet
      +    exit(
      +        (NewMeasurement.SqlStatementsCount < PreviousMeasurement.SqlStatementsCount * 0.1) and
      +        (NewMeasurement.DurationMs < PreviousMeasurement.DurationMs * 0.5)
      +    );
      +end;
      +```
      +
      +### SingleInstance and Event Subscribers
      +
      +If the task codeunit uses event subscribers and stores state in codeunit-level variables, add `SingleInstance = true`:
      +
      +```al
      +codeunit 74250 "R4 Active Customer Task" implements iEscapeRoomTask
      +{
      +    SingleInstance = true;
      +    // SingleInstance required: event subscriber sets TaskCompleted flag on this instance
      +
      +---
      +
      +## Selecting the Best Anti-Pattern Example
      +
      +When choosing which performance anti-pattern to demonstrate in a room, rank candidates by:
      +
      +1. **Clarity of fix** — The correct solution is a small, targeted code change. Not a restructuring.
      +2. **Educational commonness** — This pattern genuinely appears in real production AL code. Developers recognise it.
      +3. **Measurability** — Fixing it produces a dramatic, unambiguous drop in SQL statement count and duration.
      +
      +### Canonical Examples by Room Type
      +
      +| Room Type | Anti-Pattern | Fix | Why It Works |
      +|---|---|---|---|
      +| N+1 Queries | `CalcFields` in a loop | `SetAutoCalcFields` before `FindSet` | Clear one-liner fix; N queries → 1; extremely common in production |
      +| Bulk Transfer | Record-by-record Copy loop | `DataTransfer` object | Small change; thousands of inserts → 1 operation |
      +| Bulk Update | Field update in a loop | `ModifyAll` | One call replaces the entire loop |
      +| Profiler | Any slow procedure | Identify via AL Profiler | Teaches the discovery process, not just the fix |
      +
      +**The best N+1 example:** `CalcFields` inside a `repeat...until` loop. It has a clear, elegant fix (`SetAutoCalcFields`), produces dramatic measurable improvement (N queries → 1), and is the most common N+1 pattern real developers write.
      +
      +---
      +
      +## Companion App Starting State (Code-Based Venues Only)
      +
      +> This section applies only when a companion code app exists.
      +
      +The companion app ships with **deliberately un-optimized code** — that is the escape room starting state. The Solution HTML files describe exactly what code changes solve each problem. After designing all rooms:
      +
      +1. Write the Solution HTML files with the exact "good" code
      +2. Implement the "bad" code in the companion app (the opposite of the solution)
      +3. Verify that running the operations in a fresh environment produces the expected slowness
      +
      +**Do not add "good" code to the companion app during room design.** The version shipped to attendees must have the unoptimized code. Participants replace it when they fix each room.
      diff --git a/non production apps/EscapeRoomApp/Docs/Framework/Room-Content-HTML.md b/non production apps/EscapeRoomApp/Docs/Framework/Room-Content-HTML.md
      new file mode 100644
      index 00000000..a22ce33a
      --- /dev/null
      +++ b/non production apps/EscapeRoomApp/Docs/Framework/Room-Content-HTML.md	
      @@ -0,0 +1,402 @@
      +# Room Content: HTML Description and Solution Files
      +
      +## Overview
      +
      +Every room has two HTML files that are loaded and displayed inside Business Central:
      +
      +- **Description file** — Presents the challenge. Tells participants WHAT to fix and WHERE to look, without revealing HOW.
      +- **Solution file** — Provides the complete, step-by-step answer after the solution delay expires.
      +
      +These files are loaded via the `GetRoomDescription()` and `GetRoomSolution()` methods on `iEscapeRoom`, or embedded as BLOB fields on the Escape Room record. They render in the **Rich Text Box Page** inside BC.
      +
      +> **BC HTML Viewer Constraints:** No JavaScript, no external CSS, no emoji or emoticons. Use inline styles only. Plain HTML with ``, ``, ``, `
      `, `
        `, `
          `, ``, and `
          ` with inline `style`. + +--- + +## Description Files + +### The One Rule That Overrides All Others + +**Descriptions present the MYSTERY. Solutions reveal the ANSWER.** + +Participants must figure out HOW to solve the challenge — that is the entire point. The description exists to make the problem tangible and send them to the right place. It must never save them the thinking. + +**The self-test:** "Could they copy-paste something from this description to solve the task?" If yes, the description is too revealing. Move it to the Solution file. + +--- + +### Tone and Language + +| Do | Do NOT | +|---|---| +| "Operations are slow" | "Record-by-record loops cause slowness" | +| "We test your ability to..." | "You will learn about..." | +| "Find a better approach" | "Use SetLoadFields to fix this" | +| "Something about how the data is loaded..." | "CalcFields fires a SQL query per record" | +| "Research what AL offers for bulk operations" | "Use DataTransfer for this scenario" | + +- **Tone = "we test your skills"**, not "you will learn" +- Describe the **symptom**, not the cause +- Describe the **problem**, not the technique that fixes it +- Never name the AL method, class, or pattern that IS the solution + +--- + +### Required Structure (7 sections, in this order) + +#### 1. HTML Shell + H1 + +```html + + + + + Room X: Short Title + + + +

          Room X: Short Title

          + + + + +``` + +Use a clear, descriptive title. Examples: `Room 2: Bulk Data Operations`, `Room 3: AL Profiler`. + +--- + +#### 2. TL;DR (H2) + +2-3 sentences maximum. The elevator pitch: what's broken, what you'll do, how you prove it's fixed. Participants who read nothing else should still know what the room is about. + +```html +

          TL;DR

          +

          Fix the slow upgrade in OptimAL.PTE by finding better approaches for bulk table + transfers and field updates. Prove your optimization uses dramatically fewer database operations.

          +``` + +--- + +#### 3. The Challenge (H2) + +ONE short paragraph (2-4 sentences) that sets the scene. If the problem was experienced in a previous room, reference it. End with a single-sentence "why this matters" statement woven into the paragraph. **Do NOT list learning objectives, future rooms, or skills here.** + +```html +

          The Challenge

          +

          In Room 1, you experienced slow data operations taking far too long for 10,000 records. There + must be a better way to transfer and update large amounts of data. Slow migrations directly + impact upgrade windows and user downtime.

          +``` + +**Rules:** +- ONE paragraph — never split into sub-sections +- "Why this matters" = ONE sentence, woven in, not a separate section or subsection +- Reference previous room context if applicable +- Do NOT write "This room teaches you...", "By the end of this room you will understand...", or any similar learning-objective framing + +--- + +#### 4. Your Mission (H2) + +The core of the description. Each mission item gets an **H3 with a descriptive title** — never task numbers ("Task 1:", "Task 2:"). Items may be grouped or split based on what makes logical sense, not one-to-one with validators. + +Each mission item contains (as flowing paragraphs, NOT separate sub-sections): + +- **What to do** — the goal, the problem to solve (described mysteriously) +- **Where to look** — exact object name, procedure name, page action (inline, as part of the prose) +- **How to trigger/reproduce** — numbered `
            ` steps only when navigating BC UI +- **Hint** — a research nudge (use `Hint:`) +- **Validation** — how the system confirms success (use ``) + +Hints must be **VAGUE about technique** and **PRECISE about location**. When referencing a BC UI action, use the **exact label as it appears in Business Central, case-sensitive**. For non-toolbar actions, include the full navigation path: + +```html +

            Hint: Navigate to Actions → Analytics & Reporting and run +the Customer Order Analytics action. Something about how it retrieves data might +surprise you.

            +``` + +```html +

            Your Mission

            + +

            Optimize Table Transfer

            +

            The upgrade code in Codeunit 74391 "Upgrade OptimAL PTE" copies records from + Performance Test Customer to a Customer Archive table in a way that is extremely slow. Find a + better approach that can transfer all records much more efficiently.

            +

            Hint: AL must have better tools for data migration scenarios. Research what is + available.

            +

            Republish OptimAL.PTE (bump version, F5) and check the Performance Measurements page for + the upgrade entry. The system validates that total database operations drop below 100.

            +``` + +**Rules for Your Mission:** +- H3 headings are **descriptive names** — never "Task 1:", "Task 2:", "Step 1:", etc. +- Never build separate "Where to Look", "Validation", "Key Concepts", or "Research Topics" sections — all of that content is inline inside the mission items +- Each item is self-contained: goal + location + hint + validation in one place +- Keep numbered `
              ` steps ONLY for BC UI navigation (how to reproduce the problem) + +--- + +#### 5. Warning / Gotcha Box (OPTIONAL) + +Only for critical setup issues that would waste significant time if missed. Maximum one per room. + +```html +
              +

              Important: Make sure your launch.json includes + "dependencyPublishingOption": "Ignore" before pressing F5.

              +
              +``` + +--- + +#### 6. Update Status Reminder + +Every description must end with this (just before "What's Next", or at the very end for the final room): + +```html +

              Update Status: Not all steps are captured automatically. Hit the + Update Status button on the room page to check if you have completed + steps that were not registered yet.

              +``` + +--- + +#### 7. What's Next (H2) + +ONE sentence about the next room only. Omit for the final room in the venue. + +```html +

              What's Next

              +

              Room 3: Learn to use profiling tools to discover performance bottlenecks yourself.

              +``` + +--- + +### Sections FORBIDDEN in Descriptions + +These sections create redundancy or reveal the answer. Their content either belongs inline in mission items, belongs only in Solution files, or should be removed entirely: + +| Forbidden Section | Why | +|---|---| +| "How Task Validation Works" | Too much explanation — breaks mystery | +| "Business Impact" | Redundant filler | +| "Performance Results" / "Performance Baseline" table | Belongs in Solution files only | +| "Profiler Comparisons" | Belongs in Solution files only | +| "Skills Tested / Learning Objectives" | The mission items ARE the skills being tested | +| "Key Concepts / Research Topics" | Fold hints and research nudges into mission items | +| "Tips for Success" | Fold into mission items | +| "Real-World Application" | Filler — remove entirely | +| "Where to Look" as a standalone section | Put inline in mission items | +| "Validation" as a standalone section | Put inline in mission items | +| "Why This Room Matters" as a standalone section | One sentence in The Challenge paragraph | +| "What's Next" listing all remaining rooms | Only mention the next room, one sentence | +| Task numbers ("Task 1:", "Task 2:") in any heading | Use descriptive H3 names instead | +| "You will learn" / "This will teach you" language | Tone is "we test your" — not educational | +| Emoticons / emojis | BC HTML viewer cannot display them | +| Naming the solution technique | Keeps challenge mysterious | + +--- + +## Solution Files + +### Purpose + +Solution files provide **exact, copy-pasteable, step-by-step instructions** for completing all tasks. They teach participants exactly how to solve the challenge while explaining WHY each step matters. + +**Solution = EXACT HOW + WHY.** No ambiguity. No "try something like this." Every code block must be complete and working. + +--- + +### Required Structure + +#### 1. Main Heading + +```html +

              Solution: Room X - Task Title

              +``` + +#### 2. Introduction (optional) + +Brief paragraph explaining the overall approach. + +#### 3. Task Sections + +Each task gets its own section: + +```html +

              Optimizing Table Transfer

              +

              Replace the slow record-by-record loop with a bulk operation.

              + +

              Step 1: Locate the Code

              +
                +
              1. Open Codeunit 74390 "Upgrade OptimAL PTE"
              2. +
              3. Find the TransferDataRecordByRecord() procedure
              4. +
              + +

              Step 2: Implement the Solution

              +
              +local procedure TransferDataBulk()
              +var
              +    DataTransfer: DataTransfer;
              +begin
              +    DataTransfer.SetTables(Database::"Performance Test Customer", Database::"Customer Archive");
              +    DataTransfer.CopyFields();
              +    DataTransfer.CopyRows();
              +end;
              +
              + +

              Why This Matters:

              +
                +
              • Uses a set-based operation instead of a loop
              • +
              • Reduces 10,000 queries to 1
              • +
              • Dramatically improves performance at scale
              • +
              +``` + +**Section heading rules:** +- Use descriptive H2 names — never "Task 1:", "Task 2:", etc. +- Sub-steps use H3; explanations use H4 +- Show "Why This Matters" after each major code block + +--- + +#### 4. Forbidden Content in Solutions + +| Forbidden | Why | +|---|---| +| "Performance Results" comparison tables | Removed in practice — numbers vary by environment | +| "Profiler Comparisons" sections | Removed in practice — misleading without live data | +| Task number references in headings | Use descriptive names | + +--- + +#### 5. Code Examples + +- **Always complete and working** — participants should be able to copy-paste +- Use `
              ` for code blocks
              +- Include full procedure signatures, not fragments
              +- Include inline code comments for complex logic
              +- Show "before" (the problem) and "after" (the solution) where helpful
              +
              +---
              +
              +#### 6. Alternative Approaches
              +
              +When multiple approaches are valid, show the recommended one first:
              +
              +```html
              +

              Option 1: Bulk Transfer with DataTransfer (Recommended)

              +
              ...
              + +

              Option 2: Manual Loop with Commit

              +
              ...
              +``` + +--- + +#### 7. Warning Boxes in Solutions + +```html +
              +

              Security Warning: Hardcoding API keys is NOT a + production pattern. This is a training shortcut only.

              +
              +``` + +--- + +#### 8. Verification Section + +```html +

              Verification

              +

              After completing all tasks:

              +
                +
              • The Performance Measurements page shows the upgrade entry with fewer than 100 DB operations
              • +
              • Both duration and SQL statement count have dropped significantly
              • +
              +

              Important: Don't forget to click the Update Status button!

              +``` + +--- + +#### 9. What You've Accomplished + +```html +

              What You've Accomplished

              +

              By completing this room, you have:

              +
                +
              • Replaced record-by-record loops with set-based operations
              • +
              • Proven the improvement using built-in measurement tooling
              • +
              • Learned to recognise this class of performance problem in real code
              • +
              +

              Ready for Room 3: Now you will use the AL Profiler to discover bottlenecks yourself.

              +``` + +--- + +## Tables + +For comparisons, metrics, or structured data in solutions: + +```html +
          + + + + + + + + + + + + +
          OperationBeforeAfterImprovement
          Transfer 10,000 records~120 seconds<5 seconds24x faster
          +``` + +--- + +## File Naming Convention + +- Description: `Room[N][Topic]Description.html` — e.g., `Room2DataTransferDescription.html` +- Solution: `Room[N][Topic]Solution.html` — e.g., `Room2DataTransferSolution.html` + +--- + +## Quality Checklists + +### Description File Checklist + +- [ ] Complete HTML structure with DOCTYPE +- [ ] Clear H1 title with room number +- [ ] TL;DR (2-3 sentences max) +- [ ] The Challenge (ONE paragraph, "why this matters" woven in as one sentence) +- [ ] Your Mission with descriptive H3 headings (no task numbers) +- [ ] Each mission item is self-contained: goal + location + hint + validation inline +- [ ] Hints use the exact BC UI action label (case-sensitive); non-toolbar actions include full nav path +- [ ] No standalone Skills Tested, Key Concepts, Research Topics, Validation, or Where to Look sections +- [ ] No Performance Results or Baseline tables +- [ ] Update Status reminder present (just before What's Next) +- [ ] What's Next mentions only the next room (one sentence) +- [ ] Problem described ONCE — no repetition across sections +- [ ] Tone is "we test your skills" — no "you will learn" language +- [ ] MYSTERIOUS — no method names, no code solutions, no technical terms that reveal the answer +- [ ] No emoticons or emojis + +### Solution File Checklist + +- [ ] Clear H1 with "Solution: Room X - Title" +- [ ] Step-by-step instructions for each task +- [ ] Complete, copy-pasteable code examples +- [ ] "Why This Matters" explanations after each code block +- [ ] Verification checklist +- [ ] "What You've Accomplished" summary +- [ ] Tutorial-style, explanatory tone +- [ ] Specific object names, procedure names, and exact UI navigation +- [ ] No Performance Results comparison tables +- [ ] No Profiler Comparisons sections +- [ ] No task number references in headings (use descriptive names) +- [ ] No emoticons or emojis diff --git a/non production apps/EscapeRoomApp/Docs/README.md b/non production apps/EscapeRoomApp/Docs/README.md index f5531ef9..17adce76 100644 --- a/non production apps/EscapeRoomApp/Docs/README.md +++ b/non production apps/EscapeRoomApp/Docs/README.md @@ -25,6 +25,8 @@ Core framework architecture and patterns for developers extending the system: - [Architecture Overview](Framework/Architecture.md) - Core components and design patterns - [Creating New Rooms](Framework/Creating-Rooms.md) - Step-by-step guide for building rooms - [Task Validation System](Framework/Task-Validation.md) - How task validation and interfaces work +- [Room Content (HTML)](Framework/Room-Content-HTML.md) - Writing Description and Solution HTML files +- [Performance Room Design](Framework/Performance-Room-Design.md) - Patterns for performance-themed rooms - [Telemetry Integration](Framework/Telemetry-Integration.md) - Scoring, tracking, and Application Insights ### 🎯 [Facilitator Documentation](Facilitator/) diff --git a/non production apps/EscapeRoomApp/ReadMe.md b/non production apps/EscapeRoomApp/ReadMe.md index 8ecab417..2afa9b6f 100644 --- a/non production apps/EscapeRoomApp/ReadMe.md +++ b/non production apps/EscapeRoomApp/ReadMe.md @@ -25,6 +25,8 @@ Core framework architecture and patterns for developers extending the system: - [Architecture Overview](Docs/Framework/Architecture.md) - Core components and design patterns - [Creating New Rooms](Docs/Framework/Creating-Rooms.md) - Step-by-step guide for building rooms - [Task Validation System](Docs/Framework/Task-Validation.md) - How task validation and interfaces work +- [Room Content (HTML)](Docs/Framework/Room-Content-HTML.md) - Writing Description and Solution HTML files +- [Performance Room Design](Docs/Framework/Performance-Room-Design.md) - Patterns for performance-themed rooms - [Telemetry Integration](Docs/Framework/Telemetry-Integration.md) - Scoring, tracking, and Application Insights ### 🎯 [Facilitator Documentation](Docs/Facilitator/) @@ -58,7 +60,9 @@ Complete chronological history of all changes to the framework across all develo 1. Understand the [Architecture Overview](Docs/Framework/Architecture.md) 2. Follow [Creating New Rooms](Docs/Framework/Creating-Rooms.md) guide 3. Implement [Task Validation System](Docs/Framework/Task-Validation.md) for challenges -4. Integrate [Telemetry](Docs/Framework/Telemetry-Integration.md) for scoring +4. Write HTML content following [Room Content (HTML)](Docs/Framework/Room-Content-HTML.md) +5. For performance rooms, see [Performance Room Design](Docs/Framework/Performance-Room-Design.md) +6. Integrate [Telemetry](Docs/Framework/Telemetry-Integration.md) for scoring ### For Framework Contributors 1. Review [Developer Guide](Docs/Dev/README.md) @@ -82,4 +86,4 @@ Complete chronological history of all changes to the framework across all develo For questions, issues, or contributions related to the framework, see the project repository. -**Last Updated:** January 7, 2026 +**Last Updated:** February 22, 2026 diff --git a/non production apps/EscapeRoomApp/Src/2.Room/EscapeRoom.Page.al b/non production apps/EscapeRoomApp/Src/2.Room/EscapeRoom.Page.al index faffd24a..4f1b3f40 100644 --- a/non production apps/EscapeRoomApp/Src/2.Room/EscapeRoom.Page.al +++ b/non production apps/EscapeRoomApp/Src/2.Room/EscapeRoom.Page.al @@ -136,6 +136,16 @@ page 73922 "Escape Room" CurrPage.Update(false); end; } + action(ResetRoom) + { + Caption = 'Reset Room'; + Image = Undo; + trigger OnAction() + begin + Rec.ResetRoom(); + CurrPage.Update(false); + end; + } } area(Promoted) { @@ -151,6 +161,10 @@ page 73922 "Escape Room" { Visible = true; } + actionref(ResetRoomRef; ResetRoom) + { + Visible = true; + } } } diff --git a/non production apps/EscapeRoomApp/Src/2.Room/EscapeRoom.Table.al b/non production apps/EscapeRoomApp/Src/2.Room/EscapeRoom.Table.al index 912c9946..befb5d9b 100644 --- a/non production apps/EscapeRoomApp/Src/2.Room/EscapeRoom.Table.al +++ b/non production apps/EscapeRoomApp/Src/2.Room/EscapeRoom.Table.al @@ -90,6 +90,11 @@ table 73920 "Escape Room" var Task: Record "Escape Room Task"; begin + if Rec.Status = Rec.Status::Completed then begin + OpenNextRoom(); + exit; + end; + if Rec.Status <> Rec.Status::InProgress then exit; Task.Setrange("Venue Id", Rec."Venue Id"); @@ -261,5 +266,33 @@ table 73920 "Escape Room" exit(true); end; + procedure ResetRoom() + var + Task: Record "Escape Room Task"; + EscapeRoomMgt: Codeunit "Escape Room"; + CurrentRoom: Interface iEscapeRoom; + ConfirmManagement: Codeunit "Confirm Management"; + ResetRoomQst: Label 'Are you sure you want to reset this room? All task progress and hints will be permanently lost and there is no way back.'; + begin + if Rec.Status = Rec.Status::Locked then exit; + + if not ConfirmManagement.GetResponseOrDefault(ResetRoomQst, false) then + exit; + + Task.SetRange("Venue Id", Rec."Venue Id"); + Task.SetRange("Room Name", Rec.Name); + Task.DeleteAll(); + + CurrentRoom := Rec.Room; + EscapeRoomMgt.RefreshTasks(CurrentRoom); + + Rec.Status := Rec.Status::InProgress; + Rec."Start DateTime" := CurrentDateTime(); + Rec."Stop DateTime" := 0DT; + Rec."Solution DateTime" := 0DT; + Rec.Modify(); + Commit(); + end; + } \ No newline at end of file