diff --git a/samples/BCAgents/SalesValidationAgent/.resources/Instructions/InstructionsV1.txt b/samples/BCAgents/SalesValidationAgent/.resources/Instructions/InstructionsV1.txt
new file mode 100644
index 00000000..633461a9
--- /dev/null
+++ b/samples/BCAgents/SalesValidationAgent/.resources/Instructions/InstructionsV1.txt
@@ -0,0 +1,30 @@
+You are a sales validation agent. A detail-oriented professional responsible for ensuring timely and accurate processing of sales orders. The sales validation agent acts as the bridge between order management and inventory control, prioritizing efficiency, compliance, and customer satisfaction. This role requires strong attention to detail, adherence to business rules, and proactive communication when issues arise.
+
+## Instructions
+
+1. Find all **open** sales orders for the **specified date**.
+2. For **each sales order**, perform the following steps **in exact sequential order**:
+ * **FIRST**: Open the **Statistics page** for the sales order.
+ * **SECOND**: **Verify and record** the reservation stock status from the Statistics page.
+ * **THIRD**: **Check and record** the shipping advice setting on the sales order.
+ * **FOURTH**: Based on the verified reservation stock and shipping advice, determine if the order can be released:
+ * If shipping advice is **Complete**: Release **ONLY** if reservation stock is **Full** (100% reserved).
+ * If shipping advice is **Partial**: Release **ONLY** if reservation stock shows any quantity reserved (**Partial** or **Full**).
+ * If reservation stock is None: **DO NOT release** - flag for user review.
+ * **FIFTH**: If release criteria are met, release the document. Otherwise, flag it with the specific reason.
+3. **Repeat step 2** for each sales order found in step 1.
+4. After completing all orders for the date, provide a summary in this format:
+ ```
+ All open sales orders with shipment date [DATE] have been reviewed:
+
+ Sales orders [o1], [o2], [o3], ... were released.
+
+ Not released:
+ Sales order [####]: Reservation Status: [None/Partial/Full], Shipping Advice: [Complete/Partial]
+ Sales order [####]: Reservation Status: [None/Partial/Full], Shipping Advice: [Complete/Partial]
+
+ Please review flagged orders for further action.
+ ```
+ * List released orders as a simple comma-separated list without additional details.
+ * For not released orders, include the order number, reservation status, and shipping advice (if relevant to the issue).
+ * Keep messaging concise and actionable.
\ No newline at end of file
diff --git a/samples/BCAgents/SalesValidationAgent/README.md b/samples/BCAgents/SalesValidationAgent/README.md
new file mode 100644
index 00000000..f72fc296
--- /dev/null
+++ b/samples/BCAgents/SalesValidationAgent/README.md
@@ -0,0 +1,65 @@
+# Sales Validation Agent
+
+A sample **third-party agent** extension for Microsoft Dynamics 365 Business Central that validates and processes sales orders by checking inventory reservation and releasing eligible orders to the warehouse. It's purpose is to demonstrate how the sample agent in Business Central can be productized into an app.
+
+## Overview
+
+The Sales Validation Agent is a Copilot-powered agent that automates the routine work of reviewing open sales orders for a given shipment date, verifying that inventory is reserved, and releasing qualifying orders. It is built on the Business Central **Agent** framework and is intended as a reference implementation for partners building their own agents.
+
+---
+
+## Project Structure
+
+```
+app/
+├── Integration/ # Copilot capability registration & install logic
+├── Interaction/ # User-facing pages and page extensions
+└── Setup/ # Agent configuration, metadata, KPIs, and profile
+ ├── KPI/ # Performance tracking (orders released)
+ ├── Metadata/ # IAgentFactory / IAgentMetadata implementations
+ └── Profile/ # Role Center profile & page customizations
+```
+
+### Integration
+
+Contains the install codeunit and enum extension that register the **Sales Validation Agent** as a Copilot capability. On first install the capability is registered with the platform; on subsequent installs or upgrades the agent's instructions are refreshed.
+
+### Interaction
+
+Provides the user-facing entry point for the agent. A page extension on the **Sales Order List** adds a *Validate with Agent* action that verifies the agent is active, prompts the user for a shipment date via a dialog page, and creates an `Agent Task` with the selected date.
+
+### Setup
+
+Holds the core configuration for the agent:
+
+- **Setup table & page** – A lightweight table stores the agent's User Security ID (company-independent), and a `ConfigurationDialog` page lets admins provision or update the agent user with the correct profile and permissions. The Copilot capability must be enabled before the setup page can be opened.
+- **Setup codeunit** – Central helper that resolves the agent user, supplies default profile and access controls (`D365 READ` + `D365 SALES`), loads instructions from a resource file, and ensures the setup record exists.
+
+#### KPI
+
+Tracks agent performance metrics. A KPI table records counters per agent user (currently *Orders Released*), exposed through a `CardPart` summary page. An event subscriber on `OnAfterReleaseSalesDoc` automatically increments the counter each time the agent releases a sales order, so metrics stay up to date without any manual bookkeeping.
+
+#### Metadata
+
+Implements the `IAgentFactory` and `IAgentMetadata` interfaces required by the Business Central Agent framework. The factory provides default initials, the setup page, the Copilot capability, the default profile, and access control templates. The `ShowCanCreateAgent` method controls UI visibility for agent creation. The agent here is single-instance. The metadata codeunit supplies page IDs for setup, summary, and task message cards. An enum extension on `Agent Metadata Provider` wires these implementations into the platform.
+
+#### Profile
+
+Defines a dedicated **Sales Validation Agent (Copilot)** profile based on the *Order Processor Role Center*. Accompanying page customizations tailor the Role Center, Sales Order card, Sales Order List, Sales Order Statistics, Sales Order Subform, and SO Processor Activities pages to present only the information the agent needs.
+
+---
+
+## How It Works
+
+1. **Create the agent** – The *Sales Validation Agent* Copilot capability is registered as **Preview** and is therefore enabled by default. Open the *Sales Val. Agent Setup* configuration dialog (accessible from the agent avatar in the Role Center) to provision the agent user with the correct profile and permissions.
+2. **Assign a task** – From the **Sales Order List**, choose *Validate with Agent*, pick a shipment date, and a task is created for the agent.
+3. **Agent processes orders** – The agent reads its instructions (loaded from `Instructions/InstructionsV1.txt`), validates open sales orders for the specified shipment date, checks inventory reservation, and releases eligible orders.
+4. **KPIs are tracked** – Each time the agent releases an order, the `OnAfterReleaseSalesDoc` event subscriber increments the *Orders Released* counter, visible on the agent's summary page.
+
+---
+
+## Prerequisites
+
+- Business Central **application version 27.4** or later
+- The **Copilot & AI Capabilities** feature enabled in the environment
+- Appropriate licensing for Copilot / Agent functionality
diff --git a/samples/BCAgents/SalesValidationAgent/app.json b/samples/BCAgents/SalesValidationAgent/app.json
new file mode 100644
index 00000000..4e0dbc72
--- /dev/null
+++ b/samples/BCAgents/SalesValidationAgent/app.json
@@ -0,0 +1,35 @@
+{
+ "id": "5878e09e-13b4-4058-a9f6-4a9ee1605d6a",
+ "name": "Sales Validation Agent Sample",
+ "publisher": "Default Publisher",
+ "version": "1.0.0.0",
+ "brief": "",
+ "description": "",
+ "privacyStatement": "",
+ "EULA": "",
+ "help": "",
+ "url": "",
+ "logo": "",
+ "dependencies": [],
+ "screenshots": [],
+ "platform": "1.0.0.0",
+ "application": "27.4.0.0",
+ "idRanges": [
+ {
+ "from": 50100,
+ "to": 50149
+ }
+ ],
+ "resourceExposurePolicy": {
+ "allowDebugging": true,
+ "allowDownloadingSource": true,
+ "includeSourceInSymbolFile": true,
+ "applyToDevExtension": false
+ },
+ "runtime": "17.0",
+ "features": [
+ "NoImplicitWith"
+ ],
+ "resourceFolders": [".resources"],
+ "target": "Cloud"
+}
\ No newline at end of file
diff --git a/samples/BCAgents/SalesValidationAgent/app/Integration/SalesValAgentInstall.Codeunit.al b/samples/BCAgents/SalesValidationAgent/app/Integration/SalesValAgentInstall.Codeunit.al
new file mode 100644
index 00000000..8e8a6ff5
--- /dev/null
+++ b/samples/BCAgents/SalesValidationAgent/app/Integration/SalesValAgentInstall.Codeunit.al
@@ -0,0 +1,59 @@
+// ------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+// ------------------------------------------------------------------------------------------------
+
+namespace SalesValidationAgent.Integration;
+
+using SalesValidationAgent.Setup;
+using System.Agents;
+using System.AI;
+using System.Security.AccessControl;
+
+codeunit 50101 "Sales Val. Agent Install"
+{
+ Subtype = Install;
+ Access = Internal;
+ InherentEntitlements = X;
+ InherentPermissions = X;
+
+ trigger OnInstallAppPerDatabase()
+ var
+ SalesValAgentSetupRec: Record "Sales Val. Agent Setup";
+ begin
+ RegisterCapability();
+
+ if not SalesValAgentSetupRec.FindSet() then
+ exit;
+
+ repeat
+ InstallAgent(SalesValAgentSetupRec);
+ until SalesValAgentSetupRec.Next() = 0;
+ end;
+
+ local procedure InstallAgent(var SalesValAgentSetupRec: Record "Sales Val. Agent Setup")
+ begin
+ InstallAgentInstructions(SalesValAgentSetupRec);
+ end;
+
+ local procedure InstallAgentInstructions(var SalesValAgentSetupRec: Record "Sales Val. Agent Setup")
+ var
+ Agent: Codeunit Agent;
+ SalesValAgentSetup: Codeunit "Sales Val. Agent Setup";
+ begin
+ Agent.SetInstructions(SalesValAgentSetupRec."User Security ID", SalesValAgentSetup.GetInstructions());
+ end;
+
+ local procedure RegisterCapability()
+ var
+ CopilotCapability: Codeunit "Copilot Capability";
+ LearnMoreUrlTxt: Label 'https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/ai/ai-development-toolkit-sales-validation', Locked = true;
+ begin
+ if not CopilotCapability.IsCapabilityRegistered(Enum::"Copilot Capability"::"Sales Validation Agent") then
+ CopilotCapability.RegisterCapability(
+ Enum::"Copilot Capability"::"Sales Validation Agent",
+ Enum::"Copilot Availability"::Preview,
+ "Copilot Billing Type"::"Microsoft Billed",
+ LearnMoreUrlTxt);
+ end;
+}
\ No newline at end of file
diff --git a/samples/BCAgents/SalesValidationAgent/app/Integration/SalesValCopilotCapability.EnumExt.al b/samples/BCAgents/SalesValidationAgent/app/Integration/SalesValCopilotCapability.EnumExt.al
new file mode 100644
index 00000000..e8d759ba
--- /dev/null
+++ b/samples/BCAgents/SalesValidationAgent/app/Integration/SalesValCopilotCapability.EnumExt.al
@@ -0,0 +1,16 @@
+// ------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+// ------------------------------------------------------------------------------------------------
+
+namespace SalesValidationAgent.Integration;
+
+using System.AI;
+
+enumextension 50100 "Sales Val. Copilot Capability" extends "Copilot Capability"
+{
+ value(50100; "Sales Validation Agent")
+ {
+ Caption = 'Sales Validation Agent';
+ }
+}
\ No newline at end of file
diff --git a/samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentDatePicker.Page.al b/samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentDatePicker.Page.al
new file mode 100644
index 00000000..44058361
--- /dev/null
+++ b/samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentDatePicker.Page.al
@@ -0,0 +1,42 @@
+// ------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+// ------------------------------------------------------------------------------------------------
+
+namespace SalesValidationAgent.Interaction;
+
+///
+/// A simple dialog page that prompts the user to select a shipment date
+/// for the Sales Validation Agent to process open sales orders.
+///
+page 50102 "Sales Val. Agent Date Picker"
+{
+ PageType = StandardDialog;
+ Caption = 'Select Shipment Date';
+
+ layout
+ {
+ area(Content)
+ {
+ field(ShipmentDate; ShipmentDate)
+ {
+ Caption = 'Shipment Date';
+ ToolTip = 'Specify the shipment date to validate sales orders for.';
+ ApplicationArea = All;
+ }
+ }
+ }
+
+ var
+ ShipmentDate: Date;
+
+ procedure GetShipmentDate(): Date
+ begin
+ exit(ShipmentDate);
+ end;
+
+ procedure SetShipmentDate(NewDate: Date)
+ begin
+ ShipmentDate := NewDate;
+ end;
+}
diff --git a/samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentSalesOrders.PageExt.al b/samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentSalesOrders.PageExt.al
new file mode 100644
index 00000000..6b043ef2
--- /dev/null
+++ b/samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentSalesOrders.PageExt.al
@@ -0,0 +1,77 @@
+// ------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+// ------------------------------------------------------------------------------------------------
+
+namespace SalesValidationAgent.Interaction;
+
+using Microsoft.Sales.Document;
+using SalesValidationAgent.Setup;
+using System.Agents;
+
+///
+/// Extends the Sales Order List page to enable Sales Validation Agent task assignment.
+/// Adds an action that allows users to trigger the agent to validate and process open sales orders.
+///
+pageextension 50101 "Sales Val. Agent Sales Orders" extends "Sales Order List"
+{
+ actions
+ {
+ addlast(processing)
+ {
+ action(ValidateWithAgent)
+ {
+ Caption = 'Validate with Agent';
+ ToolTip = 'Assign a validation task to the Sales Validation Agent to process open orders for a specific date.';
+ Image = Task;
+ ApplicationArea = All;
+
+ trigger OnAction()
+ var
+ AgentTask: Record "Agent Task";
+ Agent: Codeunit Agent;
+ AgentTaskBuilder: Codeunit "Agent Task Builder";
+ SalesValAgentSetup: Codeunit "Sales Val. Agent Setup";
+ DatePicker: Page "Sales Val. Agent Date Picker";
+ AgentUserSecurityId: Guid;
+ TaskTitle: Text[150];
+ From: Text[250];
+ Message: Text;
+ ShipmentDate: Date;
+ begin
+ if not SalesValAgentSetup.TryGetAgent(AgentUserSecurityId) then
+ Error(SVAgentDoesNotExistErr);
+
+ if not Agent.IsActive(AgentUserSecurityId) then
+ Error(SVAgentNotActiveErr);
+
+ DatePicker.SetShipmentDate(WorkDate());
+ if DatePicker.RunModal() <> Action::OK then
+ exit;
+
+ ShipmentDate := DatePicker.GetShipmentDate();
+ if ShipmentDate = 0D then
+ Error(ShipmentDateRequiredErr);
+
+ Message := StrSubstNo(TaskMessageLbl, ShipmentDate);
+ TaskTitle := CopyStr(StrSubstNo(TaskTitleLbl, ShipmentDate), 1, MaxStrLen(TaskTitle));
+ From := CopyStr(UserId(), 1, MaxStrLen(From));
+
+ AgentTask := AgentTaskBuilder.Initialize(AgentUserSecurityId, TaskTitle)
+ .AddTaskMessage(From, Message)
+ .Create();
+
+ Message(TaskAssignedMsg, AgentTask.ID, ShipmentDate);
+ end;
+ }
+ }
+ }
+
+ var
+ SVAgentDoesNotExistErr: Label 'The Sales Validation Agent has not been created.';
+ SVAgentNotActiveErr: Label 'The Sales Validation Agent is not active.';
+ ShipmentDateRequiredErr: Label 'A shipment date must be specified.';
+ TaskMessageLbl: Label 'Run and process shipment date %1.', Locked = true, Comment = '%1 = Shipment Date';
+ TaskTitleLbl: Label 'Validate Sales Orders for %1', Comment = '%1 = Shipment Date';
+ TaskAssignedMsg: Label 'Task %1 assigned successfully to validate sales orders for date: %2.', Comment = '%1 = Task ID, %2 = Shipment Date';
+}
diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPI.Page.al b/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPI.Page.al
new file mode 100644
index 00000000..431649f9
--- /dev/null
+++ b/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPI.Page.al
@@ -0,0 +1,62 @@
+// ------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+// ------------------------------------------------------------------------------------------------
+
+namespace SalesValidationAgent.Setup.KPI;
+
+page 50104 "Sales Val. Agent KPI"
+{
+ PageType = CardPart;
+ ApplicationArea = All;
+ UsageCategory = Administration;
+ Caption = 'Sales Validation Agent Summary';
+ SourceTable = "Sales Val. Agent KPI";
+ Editable = false;
+ Extensible = false;
+
+ layout
+ {
+ area(Content)
+ {
+ cuegroup(KeyMetrics)
+ {
+ Caption = 'Key Performance Indicators';
+
+ field(OrdersReleased; Rec."Orders Released")
+ {
+ Caption = 'Orders Released';
+ ToolTip = 'Specifies the number of sales orders released by the agent.';
+ }
+ }
+ }
+ }
+
+ trigger OnOpenPage()
+ begin
+ GetRelevantAgent();
+ end;
+
+ ///
+ /// Retrieves the relevant agent's KPI record for display.
+ /// This page is launched via IAgentMetadata.GetSummaryPageId(). The platform sets a filter on the
+ /// "User Security ID" field before opening the page, so the source record may not be fully populated
+ /// on open - the filter is evaluated here to resolve and load the correct record.
+ ///
+ local procedure GetRelevantAgent()
+ var
+ UserSecurityIDFilter: Text;
+ begin
+ if IsNullGuid(Rec."User Security ID") then begin
+ UserSecurityIDFilter := Rec.GetFilter("User Security ID");
+ if not Evaluate(Rec."User Security ID", UserSecurityIDFilter) then
+ Error(AgentDoesNotExistErr);
+ end;
+
+ if not Rec.Get(Rec."User Security ID") then
+ Rec.Insert();
+ end;
+
+ var
+ AgentDoesNotExistErr: Label 'The agent does not exist. Please check the configuration.';
+}
diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPI.Table.al b/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPI.Table.al
new file mode 100644
index 00000000..af3270a6
--- /dev/null
+++ b/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPI.Table.al
@@ -0,0 +1,44 @@
+// ------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+// ------------------------------------------------------------------------------------------------
+
+namespace SalesValidationAgent.Setup.KPI;
+
+table 50101 "Sales Val. Agent KPI"
+{
+ Access = Internal;
+ Caption = 'Sales Val. Agent KPI';
+ DataClassification = CustomerContent;
+ InherentEntitlements = RIMDX;
+ InherentPermissions = RIMDX;
+ ReplicateData = false;
+ DataPerCompany = false;
+
+ fields
+ {
+ // This field is part of the IAgentMetadata.GetSummaryPageId() contract.
+ // The platform filters on "User Security ID" when opening the summary page,
+ // so it must be the primary key of this table.
+ field(1; "User Security ID"; Guid)
+ {
+ Caption = 'User Security ID';
+ ToolTip = 'Specifies the unique identifier for the agent user.';
+ DataClassification = EndUserPseudonymousIdentifiers;
+ Editable = false;
+ }
+ field(10; "Orders Released"; Integer)
+ {
+ Caption = 'Orders Released';
+ ToolTip = 'Specifies the number of sales orders released by the agent.';
+ DataClassification = CustomerContent;
+ }
+ }
+ keys
+ {
+ key(Key1; "User Security ID")
+ {
+ Clustered = true;
+ }
+ }
+}
diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPILogging.Codeunit.al b/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPILogging.Codeunit.al
new file mode 100644
index 00000000..8b117c11
--- /dev/null
+++ b/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPILogging.Codeunit.al
@@ -0,0 +1,64 @@
+// ------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+// ------------------------------------------------------------------------------------------------
+
+namespace SalesValidationAgent.Setup.KPI;
+
+using Microsoft.Sales.Document;
+using SalesValidationAgent.Setup;
+using System.Agents;
+
+///
+/// Subscribes to business events to automatically log KPI metrics
+/// for the Sales Validation Agent.
+///
+/// - Orders Released: incremented each time the agent releases a sales order.
+///
+codeunit 50106 "Sales Val. Agent KPI Logging"
+{
+ Access = Internal;
+ EventSubscriberInstance = StaticAutomatic;
+ SingleInstance = true;
+
+ [EventSubscriber(ObjectType::Codeunit, Codeunit::"Release Sales Document", OnAfterReleaseSalesDoc, '', false, false)]
+ local procedure OnAfterReleaseSalesDoc(var SalesHeader: Record "Sales Header")
+ var
+ AgentSession: Codeunit "Agent Session";
+ SalesValAgentSetup: Codeunit "Sales Val. Agent Setup";
+ AgentUserSecurityId: Guid;
+ AgentMetadataProvider: Enum "Agent Metadata Provider";
+ begin
+ if not AgentSession.IsAgentSession(AgentMetadataProvider) then
+ exit;
+
+ if AgentMetadataProvider <> Enum::"Agent Metadata Provider"::"Sales Validation Agent" then
+ exit;
+
+ // Only count sales orders (not quotes, invoices, etc.)
+ if SalesHeader."Document Type" <> SalesHeader."Document Type"::Order then
+ exit;
+
+ if not SalesValAgentSetup.TryGetAgent(AgentUserSecurityId) then
+ exit;
+
+ if UserSecurityId() <> AgentUserSecurityId then
+ exit;
+
+ UpdateKPI(AgentUserSecurityId, 1);
+ end;
+
+ local procedure UpdateKPI(AgentUserSecurityId: Guid; ReleasedIncrement: Integer)
+ var
+ SalesValAgentKPI: Record "Sales Val. Agent KPI";
+ begin
+ if not SalesValAgentKPI.Get(AgentUserSecurityId) then begin
+ SalesValAgentKPI.Init();
+ SalesValAgentKPI."User Security ID" := AgentUserSecurityId;
+ SalesValAgentKPI.Insert();
+ end;
+
+ SalesValAgentKPI."Orders Released" += ReleasedIncrement;
+ SalesValAgentKPI.Modify();
+ end;
+}
diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentFactory.Codeunit.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentFactory.Codeunit.al
new file mode 100644
index 00000000..ec59c964
--- /dev/null
+++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentFactory.Codeunit.al
@@ -0,0 +1,52 @@
+// ------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+// ------------------------------------------------------------------------------------------------
+
+namespace SalesValidationAgent.Setup.Metadata;
+
+using SalesValidationAgent.Setup;
+using System.Agents;
+using System.AI;
+using System.Reflection;
+using System.Security.AccessControl;
+
+codeunit 50100 SalesValAgentFactory implements IAgentFactory
+{
+ Access = Internal;
+
+ procedure GetDefaultInitials(): Text[4]
+ begin
+ exit(SalesValAgentSetup.GetInitials());
+ end;
+
+ procedure GetFirstTimeSetupPageId(): Integer
+ begin
+ exit(SalesValAgentSetup.GetSetupPageId());
+ end;
+
+ procedure ShowCanCreateAgent(): Boolean
+ var
+ SalesValAgentSetupRec: Record "Sales Val. Agent Setup";
+ begin
+ exit(SalesValAgentSetupRec.IsEmpty());
+ end;
+
+ procedure GetCopilotCapability(): Enum "Copilot Capability"
+ begin
+ exit("Copilot Capability"::"Sales Validation Agent");
+ end;
+
+ procedure GetDefaultProfile(var TempAllProfile: Record "All Profile" temporary)
+ begin
+ SalesValAgentSetup.GetDefaultProfile(TempAllProfile);
+ end;
+
+ procedure GetDefaultAccessControls(var TempAccessControlTemplate: Record "Access Control Buffer" temporary)
+ begin
+ SalesValAgentSetup.GetDefaultAccessControls(TempAccessControlTemplate);
+ end;
+
+ var
+ SalesValAgentSetup: Codeunit "Sales Val. Agent Setup";
+}
\ No newline at end of file
diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentMetadata.Codeunit.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentMetadata.Codeunit.al
new file mode 100644
index 00000000..357e0623
--- /dev/null
+++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentMetadata.Codeunit.al
@@ -0,0 +1,42 @@
+// ------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+// ------------------------------------------------------------------------------------------------
+
+namespace SalesValidationAgent.Setup.Metadata;
+
+using SalesValidationAgent.Setup;
+using System.Agents;
+
+codeunit 50102 SalesValAgentMetadata implements IAgentMetadata
+{
+ Access = Internal;
+
+ procedure GetInitials(AgentUserId: Guid): Text[4]
+ begin
+ exit(SalesValAgentSetup.GetInitials());
+ end;
+
+ procedure GetSetupPageId(AgentUserId: Guid): Integer
+ begin
+ exit(SalesValAgentSetup.GetSetupPageId());
+ end;
+
+ procedure GetSummaryPageId(AgentUserId: Guid): Integer
+ begin
+ exit(SalesValAgentSetup.GetSummaryPageId());
+ end;
+
+ procedure GetAgentTaskMessagePageId(AgentUserId: Guid; MessageId: Guid): Integer
+ begin
+ exit(Page::"Agent Task Message Card");
+ end;
+
+ procedure GetAgentAnnotations(AgentUserId: Guid; var Annotations: Record "Agent Annotation")
+ begin
+ Clear(Annotations);
+ end;
+
+ var
+ SalesValAgentSetup: Codeunit "Sales Val. Agent Setup";
+}
\ No newline at end of file
diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValMetadataProvider.EnumExt.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValMetadataProvider.EnumExt.al
new file mode 100644
index 00000000..14a14701
--- /dev/null
+++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValMetadataProvider.EnumExt.al
@@ -0,0 +1,17 @@
+// ------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+// ------------------------------------------------------------------------------------------------
+
+namespace SalesValidationAgent.Setup.Metadata;
+
+using System.Agents;
+
+enumextension 50101 "Sales Val. Metadata Provider" extends "Agent Metadata Provider"
+{
+ value(50101; "Sales Validation Agent")
+ {
+ Caption = 'Sales Validation Agent';
+ Implementation = IAgentFactory = SalesValAgentFactory, IAgentMetadata = SalesValAgentMetadata;
+ }
+}
\ No newline at end of file
diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVOrderProcessorRC.PageCustomization.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVOrderProcessorRC.PageCustomization.al
new file mode 100644
index 00000000..90f9c044
--- /dev/null
+++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVOrderProcessorRC.PageCustomization.al
@@ -0,0 +1,30 @@
+// ------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+// ------------------------------------------------------------------------------------------------
+
+namespace SalesValidationAgent.Setup.Profile;
+
+using Microsoft.Sales.RoleCenters;
+
+
+pagecustomization "SV Order Processor RC" customizes "Order Processor Role Center"
+{
+ ClearLayout = true;
+ ClearActions = true;
+
+ layout
+ {
+ modify(Control1901851508)
+ {
+ Visible = true;
+ }
+ }
+ actions
+ {
+ modify("Sales Orders")
+ {
+ Visible = true;
+ }
+ }
+}
diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSOProcessorActivities.PageCustomization.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSOProcessorActivities.PageCustomization.al
new file mode 100644
index 00000000..a16b0e20
--- /dev/null
+++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSOProcessorActivities.PageCustomization.al
@@ -0,0 +1,26 @@
+// ------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+// ------------------------------------------------------------------------------------------------
+
+namespace SalesValidationAgent.Setup.Profile;
+
+using Microsoft.Sales.RoleCenters;
+
+pagecustomization "SV SO Processor Activities" customizes "SO Processor Activities"
+{
+ ClearLayout = true;
+ ClearActions = true;
+
+ layout
+ {
+ modify("Sales Orders - Open")
+ {
+ Visible = true;
+ }
+ modify(SalesOrdersReservedFromStock)
+ {
+ Visible = true;
+ }
+ }
+}
diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrder.PageCustomization.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrder.PageCustomization.al
new file mode 100644
index 00000000..4d4a0bca
--- /dev/null
+++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrder.PageCustomization.al
@@ -0,0 +1,61 @@
+// ------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+// ------------------------------------------------------------------------------------------------
+
+namespace SalesValidationAgent.Setup.Profile;
+
+using Microsoft.Sales.Document;
+
+pagecustomization "SV Sales Order" customizes "Sales Order"
+{
+ ClearLayout = true;
+ ClearActions = true;
+
+ layout
+ {
+ modify("Sell-to")
+ {
+ Visible = false;
+ }
+ modify(Status)
+ {
+ Visible = true;
+ }
+ modify(SalesLines)
+ {
+ Visible = true;
+ }
+ modify("Shipping and Billing")
+ {
+ Visible = true;
+ }
+ modify("Location Code")
+ {
+ Visible = true;
+ }
+ modify("Shipping Advice")
+ {
+ Visible = true;
+ }
+ modify("Shipment Date")
+ {
+ Visible = true;
+ }
+ }
+ actions
+ {
+ modify(Release)
+ {
+ Visible = true;
+ }
+ modify(Reopen)
+ {
+ Visible = true;
+ }
+ modify(SalesOrderStatistics)
+ {
+ Visible = true;
+ }
+ }
+}
diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderList.PageCustomization.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderList.PageCustomization.al
new file mode 100644
index 00000000..8d3ed048
--- /dev/null
+++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderList.PageCustomization.al
@@ -0,0 +1,62 @@
+// ------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+// ------------------------------------------------------------------------------------------------
+
+namespace SalesValidationAgent.Setup.Profile;
+
+using Microsoft.Sales.Document;
+
+pagecustomization "SV Sales Order List" customizes "Sales Order List"
+{
+ ClearLayout = true;
+ ClearActions = true;
+ ClearViews = true;
+
+ layout
+ {
+ modify("No.")
+ {
+ Visible = true;
+ }
+ modify("Sell-to Customer No.")
+ {
+ Visible = true;
+ }
+ modify("Sell-to Customer Name")
+ {
+ Visible = true;
+ }
+ modify("Location Code")
+ {
+ Visible = true;
+ }
+ modify(Status)
+ {
+ Visible = true;
+ }
+ modify("Shipment Date")
+ {
+ Visible = true;
+ }
+ modify("Completely Shipped")
+ {
+ Visible = true;
+ }
+ }
+ actions
+ {
+ modify(Release)
+ {
+ Visible = true;
+ }
+ modify(Reopen)
+ {
+ Visible = true;
+ }
+ modify(SalesOrderStatistics)
+ {
+ Visible = true;
+ }
+ }
+}
diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderStatistics.PageCustomization.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderStatistics.PageCustomization.al
new file mode 100644
index 00000000..c4b0d3f7
--- /dev/null
+++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderStatistics.PageCustomization.al
@@ -0,0 +1,26 @@
+// ------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+// ------------------------------------------------------------------------------------------------
+
+namespace SalesValidationAgent.Setup.Profile;
+
+using Microsoft.Sales.Document;
+
+pagecustomization "SV Sales Order Statistics" customizes "Sales Order Statistics"
+{
+ ClearLayout = true;
+ ClearActions = true;
+
+ layout
+ {
+ modify(General)
+ {
+ Visible = true;
+ }
+ modify("Reserved From Stock")
+ {
+ Visible = true;
+ }
+ }
+}
diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderSubform.PageCustomization.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderSubform.PageCustomization.al
new file mode 100644
index 00000000..ca783c26
--- /dev/null
+++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderSubform.PageCustomization.al
@@ -0,0 +1,46 @@
+// ------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+// ------------------------------------------------------------------------------------------------
+
+namespace SalesValidationAgent.Setup.Profile;
+
+using Microsoft.Sales.Document;
+
+pagecustomization "SV Sales Order Subform" customizes "Sales Order Subform"
+{
+ ClearLayout = true;
+ ClearActions = true;
+
+ layout
+ {
+ modify(Type)
+ {
+ Visible = true;
+ }
+ modify("No.")
+ {
+ Visible = true;
+ }
+ modify(Description)
+ {
+ Visible = true;
+ }
+ modify("Location Code")
+ {
+ Visible = true;
+ }
+ modify(Quantity)
+ {
+ Visible = true;
+ }
+ modify("Reserved Quantity")
+ {
+ Visible = true;
+ }
+ modify("Shipment Date")
+ {
+ Visible = true;
+ }
+ }
+}
diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SalesValidationAgent.Profile.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SalesValidationAgent.Profile.al
new file mode 100644
index 00000000..8dd14bdf
--- /dev/null
+++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SalesValidationAgent.Profile.al
@@ -0,0 +1,18 @@
+// ------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+// ------------------------------------------------------------------------------------------------
+
+namespace SalesValidationAgent.Setup.Profile;
+
+using Microsoft.Sales.RoleCenters;
+
+profile "Sales Validation Agent"
+{
+ Caption = 'Sales Validation Agent (Copilot)';
+ Enabled = true;
+ ProfileDescription = 'Functionality for the Sales Validation Agent to efficiently validate and process sales orders.';
+ Promoted = false;
+ RoleCenter = "Order Processor Role Center";
+ Customizations = "SV Order Processor RC", "SV Sales Order", "SV Sales Order Statistics", "SV SO Processor Activities", "SV Sales Order List", "SV Sales Order Subform";
+}
diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Codeunit.al b/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Codeunit.al
new file mode 100644
index 00000000..8801e225
--- /dev/null
+++ b/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Codeunit.al
@@ -0,0 +1,95 @@
+// ------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+// ------------------------------------------------------------------------------------------------
+
+namespace SalesValidationAgent.Setup;
+
+using SalesValidationAgent.Setup.KPI;
+using System.Agents;
+using System.Reflection;
+using System.Security.AccessControl;
+
+codeunit 50103 "Sales Val. Agent Setup"
+{
+ Access = Internal;
+
+ procedure TryGetAgent(var AgentUserSecurityId: Guid): Boolean
+ var
+ SalesValAgentSetupRec: Record "Sales Val. Agent Setup";
+ begin
+ if SalesValAgentSetupRec.FindFirst() then begin
+ AgentUserSecurityId := SalesValAgentSetupRec."User Security ID";
+ exit(true);
+ end;
+
+ exit(false);
+ end;
+
+ procedure GetInitials(): Text[4]
+ begin
+ exit(AgentInitialsLbl);
+ end;
+
+ procedure GetSetupPageId(): Integer
+ begin
+ exit(Page::"Sales Val. Agent Setup");
+ end;
+
+ procedure GetSummaryPageId(): Integer
+ begin
+ exit(Page::"Sales Val. Agent KPI");
+ end;
+
+ procedure EnsureSetupExists(UserSecurityID: Guid)
+ var
+ SalesValAgentSetupRec: Record "Sales Val. Agent Setup";
+ begin
+ if not SalesValAgentSetupRec.Get(UserSecurityID) then begin
+ SalesValAgentSetupRec."User Security ID" := UserSecurityID;
+ SalesValAgentSetupRec.Insert();
+ end;
+ end;
+
+ [NonDebuggable]
+ procedure GetInstructions(): SecretText
+ var
+ Instructions: Text;
+ begin
+ Instructions := NavApp.GetResourceAsText('Instructions/InstructionsV1.txt');
+ exit(Instructions);
+ end;
+
+ procedure GetDefaultProfile(var TempAllProfile: Record "All Profile" temporary)
+ var
+ CurrentModuleInfo: ModuleInfo;
+ begin
+ NavApp.GetCurrentModuleInfo(CurrentModuleInfo);
+ Agent.PopulateDefaultProfile(DefaultProfileTok, CurrentModuleInfo.Id, TempAllProfile);
+ end;
+
+ procedure GetDefaultAccessControls(var TempAccessControlBuffer: Record "Access Control Buffer" temporary)
+ begin
+ Clear(TempAccessControlBuffer);
+ TempAccessControlBuffer."Company Name" := CopyStr(CompanyName(), 1, MaxStrLen(TempAccessControlBuffer."Company Name"));
+ TempAccessControlBuffer.Scope := TempAccessControlBuffer.Scope::System;
+ TempAccessControlBuffer."App ID" := BaseApplicationAppIdTok;
+ TempAccessControlBuffer."Role ID" := D365ReadPermissionSetTok;
+ TempAccessControlBuffer.Insert();
+
+ TempAccessControlBuffer.Init();
+ TempAccessControlBuffer."Company Name" := CopyStr(CompanyName(), 1, MaxStrLen(TempAccessControlBuffer."Company Name"));
+ TempAccessControlBuffer.Scope := TempAccessControlBuffer.Scope::System;
+ TempAccessControlBuffer."App ID" := BaseApplicationAppIdTok;
+ TempAccessControlBuffer."Role ID" := D365SalesPermissionSetTok;
+ TempAccessControlBuffer.Insert();
+ end;
+
+ var
+ Agent: Codeunit Agent;
+ DefaultProfileTok: Label 'SALES VALIDATION AGENT', Locked = true;
+ AgentInitialsLbl: Label 'SV', MaxLength = 4;
+ BaseApplicationAppIdTok: Label '437dbf0e-84ff-417a-965d-ed2bb9650972', Locked = true;
+ D365ReadPermissionSetTok: Label 'D365 READ', Locked = true;
+ D365SalesPermissionSetTok: Label 'D365 SALES', Locked = true;
+}
\ No newline at end of file
diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Page.al b/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Page.al
new file mode 100644
index 00000000..a34eead7
--- /dev/null
+++ b/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Page.al
@@ -0,0 +1,134 @@
+// ------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+// ------------------------------------------------------------------------------------------------
+
+namespace SalesValidationAgent.Setup;
+
+using System.Agents;
+using System.AI;
+
+page 50100 "Sales Val. Agent Setup"
+{
+ PageType = ConfigurationDialog;
+ Extensible = false;
+ ApplicationArea = All;
+ IsPreview = true;
+ Caption = 'Set up Sales Validation Agent';
+ InstructionalText = 'The Sales Validation Agent validates and processes sales orders by checking inventory reservation and releasing eligible orders to the warehouse.';
+ AdditionalSearchTerms = 'Sales Validation Agent, Agent';
+ SourceTable = "Sales Val. Agent Setup";
+ SourceTableTemporary = true;
+ InherentEntitlements = X;
+ InherentPermissions = X;
+
+ layout
+ {
+ area(Content)
+ {
+ part(AgentSetupPart; "Agent Setup Part")
+ {
+ ApplicationArea = All;
+ UpdatePropagation = Both;
+ }
+ }
+ }
+ actions
+ {
+ area(SystemActions)
+ {
+ systemaction(OK)
+ {
+ Caption = 'Update';
+ Enabled = IsUpdated;
+ ToolTip = 'Apply the changes to the agent setup.';
+ }
+
+ systemaction(Cancel)
+ {
+ Caption = 'Cancel';
+ ToolTip = 'Discards the changes and closes the setup page.';
+ }
+ }
+ }
+
+ trigger OnOpenPage()
+ begin
+ if not AzureOpenAI.IsEnabled(Enum::"Copilot Capability"::"Sales Validation Agent") then
+ Error(SalesValAgentNotEnabledErr);
+
+ IsUpdated := false;
+ InitializePage();
+ end;
+
+ trigger OnAfterGetRecord()
+ begin
+ InitializePage();
+ end;
+
+ trigger OnAfterGetCurrRecord()
+ begin
+ IsUpdated := IsUpdated or CurrPage.AgentSetupPart.Page.GetChangesMade();
+ end;
+
+ trigger OnModifyRecord(): Boolean
+ begin
+ IsUpdated := true;
+ end;
+
+ trigger OnQueryClosePage(CloseAction: Action): Boolean
+ var
+ Agent: Codeunit Agent;
+ SalesValAgentSetup: Codeunit "Sales Val. Agent Setup";
+ begin
+ if CloseAction = CloseAction::Cancel then
+ exit(true);
+
+ CurrPage.AgentSetupPart.Page.GetAgentSetupBuffer(AgentSetupBuffer);
+
+ if IsNullGuid(AgentSetupBuffer."User Security ID") then
+ AgentSetupBuffer."Agent Metadata Provider" := Enum::"Agent Metadata Provider"::"Sales Validation Agent";
+
+ if GlobalAgentSetup.GetChangesMade(AgentSetupBuffer) then begin
+ Rec."User Security ID" := GlobalAgentSetup.SaveChanges(AgentSetupBuffer);
+
+ Agent.SetInstructions(Rec."User Security ID", SalesValAgentSetup.GetInstructions());
+ end;
+
+ SalesValAgentSetup.EnsureSetupExists(Rec."User Security ID");
+ exit(true);
+ end;
+
+ local procedure InitializePage()
+ var
+ AgentSetup: Codeunit "Agent Setup";
+ begin
+ if Rec.IsEmpty() then
+ Rec.Insert();
+
+ CurrPage.AgentSetupPart.Page.GetAgentSetupBuffer(AgentSetupBuffer);
+ if AgentSetupBuffer.IsEmpty() then
+ AgentSetup.GetSetupRecord(
+ AgentSetupBuffer,
+ Rec."User Security ID",
+ Enum::"Agent Metadata Provider"::"Sales Validation Agent",
+ AgentNameLbl + ' - ' + CompanyName(),
+ DefaultDisplayNameLbl,
+ AgentSummaryLbl);
+
+ CurrPage.AgentSetupPart.Page.SetAgentSetupBuffer(AgentSetupBuffer);
+ CurrPage.AgentSetupPart.Page.Update(false);
+
+ IsUpdated := IsUpdated or CurrPage.AgentSetupPart.Page.GetChangesMade();
+ end;
+
+ var
+ AgentSetupBuffer: Record "Agent Setup Buffer";
+ GlobalAgentSetup: Codeunit "Agent Setup";
+ AzureOpenAI: Codeunit "Azure OpenAI";
+ IsUpdated: Boolean;
+ SalesValAgentNotEnabledErr: Label 'The Sales Validation Agent capability is not enabled in Copilot capabilities.\\Please enable the capability before setting up the agent.';
+ AgentNameLbl: Label 'Sales Validation Agent';
+ DefaultDisplayNameLbl: Label 'Sales Validation Agent';
+ AgentSummaryLbl: Label 'Validates and processes sales orders by checking inventory reservation and releasing eligible orders to the warehouse.';
+}
\ No newline at end of file
diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Table.al b/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Table.al
new file mode 100644
index 00000000..ba3c0473
--- /dev/null
+++ b/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Table.al
@@ -0,0 +1,38 @@
+// ------------------------------------------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License. See License.txt in the project root for license information.
+// ------------------------------------------------------------------------------------------------
+
+namespace SalesValidationAgent.Setup;
+
+table 50100 "Sales Val. Agent Setup"
+{
+ Access = Internal;
+ Caption = 'Sales Val. Agent Setup';
+ DataClassification = CustomerContent;
+ InherentEntitlements = RIMDX;
+ InherentPermissions = RIMDX;
+ ReplicateData = false;
+ DataPerCompany = false;
+
+ fields
+ {
+ // The platform uses a field named "User Security ID" to open the setup and summary pages
+ // defined in IAgentMetadata. This field must exist with this exact name on the source table.
+ field(1; "User Security ID"; Guid)
+ {
+ Caption = 'User Security ID';
+ ToolTip = 'Specifies the unique identifier for the user.';
+ DataClassification = EndUserPseudonymousIdentifiers;
+ Editable = false;
+ }
+ }
+
+ keys
+ {
+ key(Key1; "User Security ID")
+ {
+ Clustered = true;
+ }
+ }
+}
\ No newline at end of file