This page documents the contract between the AL side (the hosting page) and the React side (the control add-in). Treat it as the source of truth when changing either layer.Documentation Index
Fetch the complete documentation index at: https://human-resource-docs.ha-consultancy.com/llms.txt
Use this file to discover all available pages before exploring further.
How a control add-in works
- The AL page declares a
usercontrol(Name; "Control Add-in Name")field in its layout. - The Control Add-in object (
controls/RosterGridHAC.al) declares the JS files to load, the event names it raises (calls from AL into JS), and the procedures it exposes (calls from JS into AL). - BC injects an iframe sourcing the bundle’s HTML wrapper. The React app boots inside.
- The control’s
Scriptsproperty points at/scripts/assets/js/base.js— the Vite output.
The event contract
Outbound (React → AL)
Every call isMicrosoft.Dynamics.NAV.InvokeExtensibilityMethod(eventName, [parameters]). The AL trigger with the matching name runs, reads data from BC tables, builds a JsonArray or JsonObject, and calls a control-add-in procedure (back into JS) with the result.
| Event name | Triggered by | Purpose |
|---|---|---|
ControlReady | scripts/startup.js once the iframe is ready | Tells AL the JS layer is up; AL then calls InitControls() |
GetShiftCodes | getShiftCodesAsync() | Asks AL for shift configurations marked Add to Roster = true |
GetRosterEntries | getRosterEntriesAsync(from, to) | Asks AL for roster entries within a date window |
SaveRosterEntry | saveRosterEntryAsync(empNo, date, shiftCode, entryNo?) | Insert or update an Employee Roster HAC row |
DeleteRosterEntry | deleteRosterEntryAsync(entryNo) | Delete the row by entry number |
GetResources | getAllResourcesAsync(from, to) | Return the BC Employee list |
GetProjects, CheckIfUserCanEdit, NewTask, CreateTaskPlanningLines, etc.) are also declared and fire when that screen is re-mounted.
Inbound (AL → React)
The control add-in declares procedures, which BC compiles into JavaScript callbacks that the AL page calls withCurrPage.Control.<ProcedureName>(<Args>). The React app registers each as a window.<NAME_IN_UPPER_SNAKE> function in dataService.ts.
| Procedure | Called from AL | Purpose |
|---|---|---|
InitControls() | After ControlReady event | Signals JS to start fetching initial data |
GET_SHIFT_CODES(JsonArray) | GetShiftCodes trigger | Hands the shift list back to React |
GET_ROSTER_ENTRIES(JsonArray) | GetRosterEntries trigger | Hands the entries back |
SAVE_ROSTER_ENTRY(JsonObject) | SaveRosterEntry trigger | Returns the saved row (with the new entryNo on insert) |
DELETE_ROSTER_ENTRY() | DeleteRosterEntry trigger | Acknowledges deletion |
GET_RESOURCES(JsonArray) | GetResources trigger | Hands the resource/employee list back |
The startup handshake
Working with the bridge in code
Bundle layout (after npm run build)
Updating the contract
When you add a new feature that needs a new event or procedure:- Add the event/procedure to
controls/RosterGridHAC.al. - Implement the AL trigger on the hosting page (
Pag70003175). - Add the matching service function in
dataService.ts. - Rebuild the React bundle (
npm run build). - Rebuild the AL
.app. - Republish.

