From 2fb1fbcbee19b9891ae3d5ca0745e5b7bf16dd99 Mon Sep 17 00:00:00 2001 From: Sotiris Dragonas Date: Fri, 20 Feb 2026 10:03:03 -0800 Subject: [PATCH 1/7] Added sales validation agent --- .../Instructions/InstructionsV1.txt | 30 ++++ .../BCAgents/SalesValidationAgent/README.md | 66 +++++++++ .../BCAgents/SalesValidationAgent/app.json | 35 +++++ .../SalesValAgentInstall.Codeunit.al | 54 ++++++++ .../SalesValCopilotCapability.EnumExt.al | 11 ++ .../SalesValAgentDatePicker.Page.al | 37 +++++ .../SalesValAgentSalesOrders.PageExt.al | 81 +++++++++++ .../app/Setup/KPI/SalesValAgentKPI.Page.al | 51 +++++++ .../app/Setup/KPI/SalesValAgentKPI.Table.al | 36 +++++ .../KPI/SalesValAgentKPILogging.Codeunit.al | 58 ++++++++ .../Metadata/SalesValAgentFactory.Codeunit.al | 48 +++++++ .../SalesValAgentMetadata.Codeunit.al | 37 +++++ .../SalesValMetadataProvider.EnumExt.al | 12 ++ .../SVOrderProcessorRC.PageCustomization.al | 25 ++++ ...SOProcessorActivities.PageCustomization.al | 21 +++ .../Profile/SVSalesOrder.PageCustomization.al | 56 ++++++++ .../SVSalesOrderList.PageCustomization.al | 57 ++++++++ ...VSalesOrderStatistics.PageCustomization.al | 21 +++ .../SVSalesOrderSubform.PageCustomization.al | 41 ++++++ .../Profile/SalesValidationAgent.Profile.al | 13 ++ .../app/Setup/SalesValAgentSetup.Codeunit.al | 90 ++++++++++++ .../app/Setup/SalesValAgentSetup.Page.al | 129 ++++++++++++++++++ .../app/Setup/SalesValAgentSetup.Table.al | 30 ++++ 23 files changed, 1039 insertions(+) create mode 100644 samples/BCAgents/SalesValidationAgent/.resources/Instructions/InstructionsV1.txt create mode 100644 samples/BCAgents/SalesValidationAgent/README.md create mode 100644 samples/BCAgents/SalesValidationAgent/app.json create mode 100644 samples/BCAgents/SalesValidationAgent/app/Integration/SalesValAgentInstall.Codeunit.al create mode 100644 samples/BCAgents/SalesValidationAgent/app/Integration/SalesValCopilotCapability.EnumExt.al create mode 100644 samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentDatePicker.Page.al create mode 100644 samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentSalesOrders.PageExt.al create mode 100644 samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPI.Page.al create mode 100644 samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPI.Table.al create mode 100644 samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPILogging.Codeunit.al create mode 100644 samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentFactory.Codeunit.al create mode 100644 samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentMetadata.Codeunit.al create mode 100644 samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValMetadataProvider.EnumExt.al create mode 100644 samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVOrderProcessorRC.PageCustomization.al create mode 100644 samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSOProcessorActivities.PageCustomization.al create mode 100644 samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrder.PageCustomization.al create mode 100644 samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderList.PageCustomization.al create mode 100644 samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderStatistics.PageCustomization.al create mode 100644 samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderSubform.PageCustomization.al create mode 100644 samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SalesValidationAgent.Profile.al create mode 100644 samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Codeunit.al create mode 100644 samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Page.al create mode 100644 samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Table.al 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..d3295327 --- /dev/null +++ b/samples/BCAgents/SalesValidationAgent/README.md @@ -0,0 +1,66 @@ +# 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, and enforces a **single-instance** constraint (only one agent can be created). 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. **Enable the capability** – An admin enables *Sales Validation Agent* in the Copilot & AI Capabilities page. +2. **Create the agent** – Open the *Sales Val. Agent Setup* configuration dialog to provision the agent user with the correct profile and permissions. +3. **Assign a task** – From the **Sales Order List**, choose *Validate with Agent*, pick a shipment date, and a task is created for the agent. +4. **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. +5. **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..abf5300a --- /dev/null +++ b/samples/BCAgents/SalesValidationAgent/app.json @@ -0,0 +1,35 @@ +{ + "id": "5878e09e-13b4-4058-a9f6-4a9ee1605d6a", + "name": "SalesValidationAgent", + "publisher": "ThirdPartyPublisher", + "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..7f0e2635 --- /dev/null +++ b/samples/BCAgents/SalesValidationAgent/app/Integration/SalesValAgentInstall.Codeunit.al @@ -0,0 +1,54 @@ +namespace ThirdPartyPublisher.SalesValidationAgent.Integration; + +using System.Agents; +using System.AI; +using System.Security.AccessControl; +using ThirdPartyPublisher.SalesValidationAgent.Setup; + +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..fdaba8b7 --- /dev/null +++ b/samples/BCAgents/SalesValidationAgent/app/Integration/SalesValCopilotCapability.EnumExt.al @@ -0,0 +1,11 @@ +namespace ThirdPartyPublisher.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..9187d79d --- /dev/null +++ b/samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentDatePicker.Page.al @@ -0,0 +1,37 @@ +namespace ThirdPartyPublisher.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..4abed5f8 --- /dev/null +++ b/samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentSalesOrders.PageExt.al @@ -0,0 +1,81 @@ +namespace ThirdPartyPublisher.SalesValidationAgent.Interaction; + +using Microsoft.Sales.Document; +using System.Agents; +using ThirdPartyPublisher.SalesValidationAgent.Setup; + +/// +/// 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; + ExternalId: Text; + 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)); + + AgentTaskBuilder.Initialize(AgentUserSecurityId, TaskTitle); + ExternalId := Format(CreateGuid()); + AgentTaskBuilder.SetExternalId(ExternalId); + AgentTaskBuilder.AddTaskMessage(From, Message); + AgentTaskBuilder.Create(); + + AgentTask.ReadIsolation(IsolationLevel::ReadCommitted); + AgentTask.SetRange("External ID", ExternalId); + if not AgentTask.FindFirst() then + Error(TaskCreateFailedErr); + + 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.'; + TaskCreateFailedErr: Label 'The agent task could not be created.'; + 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..65ace51c --- /dev/null +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPI.Page.al @@ -0,0 +1,51 @@ +namespace ThirdPartyPublisher.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; + + 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..d10d596b --- /dev/null +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPI.Table.al @@ -0,0 +1,36 @@ +namespace ThirdPartyPublisher.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 + { + 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..00bd1d5d --- /dev/null +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPILogging.Codeunit.al @@ -0,0 +1,58 @@ +namespace ThirdPartyPublisher.SalesValidationAgent.Setup.KPI; + +using Microsoft.Sales.Document; +using System.Agents; +using ThirdPartyPublisher.SalesValidationAgent.Setup; + +/// +/// 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; + 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..3bf1bb8f --- /dev/null +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentFactory.Codeunit.al @@ -0,0 +1,48 @@ +namespace ThirdPartyPublisher.SalesValidationAgent.Setup.Metadata; + +using System.Agents; +using System.AI; +using System.Reflection; +using System.Security.AccessControl; +using ThirdPartyPublisher.SalesValidationAgent.Setup; + +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 + // Single instance agent + exit(not SalesValAgentSetupRec.FindFirst()); + 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..f5033b8a --- /dev/null +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentMetadata.Codeunit.al @@ -0,0 +1,37 @@ +namespace ThirdPartyPublisher.SalesValidationAgent.Setup.Metadata; + +using System.Agents; +using ThirdPartyPublisher.SalesValidationAgent.Setup; + +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..e345f50a --- /dev/null +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValMetadataProvider.EnumExt.al @@ -0,0 +1,12 @@ +namespace ThirdPartyPublisher.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..ee2149f7 --- /dev/null +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVOrderProcessorRC.PageCustomization.al @@ -0,0 +1,25 @@ +namespace ThirdPartyPublisher.SalesValidationAgent.Setup.Profile; + +using Microsoft.Sales.RoleCenters; + + +pagecustomization SVOrderProcessorRC 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..e3a6baec --- /dev/null +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSOProcessorActivities.PageCustomization.al @@ -0,0 +1,21 @@ +namespace ThirdPartyPublisher.SalesValidationAgent.Setup.Profile; + +using Microsoft.Sales.RoleCenters; + +pagecustomization SVSOProcessorActivities 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..6e7b5c12 --- /dev/null +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrder.PageCustomization.al @@ -0,0 +1,56 @@ +namespace ThirdPartyPublisher.SalesValidationAgent.Setup.Profile; + +using Microsoft.Sales.Document; + +pagecustomization SVSalesOrder 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..f3b31834 --- /dev/null +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderList.PageCustomization.al @@ -0,0 +1,57 @@ +namespace ThirdPartyPublisher.SalesValidationAgent.Setup.Profile; + +using Microsoft.Sales.Document; + +pagecustomization SVSalesOrderList 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..b2d99cd2 --- /dev/null +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderStatistics.PageCustomization.al @@ -0,0 +1,21 @@ +namespace ThirdPartyPublisher.SalesValidationAgent.Setup.Profile; + +using Microsoft.Sales.Document; + +pagecustomization SVSalesOrderStatistics 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..cf13dbdf --- /dev/null +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderSubform.PageCustomization.al @@ -0,0 +1,41 @@ +namespace ThirdPartyPublisher.SalesValidationAgent.Setup.Profile; + +using Microsoft.Sales.Document; + +pagecustomization SVSalesOrderSubform 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..648f07bb --- /dev/null +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SalesValidationAgent.Profile.al @@ -0,0 +1,13 @@ +namespace ThirdPartyPublisher.SalesValidationAgent.Setup.Profile; + +using Microsoft.Sales.RoleCenters; + +profile "Sales Validation Agent" +{ + Caption = 'Sales Validation Agent (Copilot)'; + Enabled = false; + ProfileDescription = 'Functionality for the Sales Validation Agent to efficiently validate and process sales orders.'; + Promoted = false; + RoleCenter = "Order Processor Role Center"; + Customizations = SVOrderProcessorRC, SVSalesOrder, SVSalesOrderStatistics, SVSOProcessorActivities, SVSalesOrderList, SVSalesOrderSubform; +} 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..969e4187 --- /dev/null +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Codeunit.al @@ -0,0 +1,90 @@ +namespace ThirdPartyPublisher.SalesValidationAgent.Setup; + +using System.Agents; +using System.Reflection; +using System.Security.AccessControl; +using ThirdPartyPublisher.SalesValidationAgent.Setup.KPI; + +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; + + internal procedure GetDefaultProfile(var TempAllProfile: Record "All Profile" temporary) + var + CurrentModuleInfo: ModuleInfo; + begin + NavApp.GetCurrentModuleInfo(CurrentModuleInfo); + Agent.PopulateDefaultProfile(DefaultProfileTok, CurrentModuleInfo.Id, TempAllProfile); + end; + + internal 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..29b35b1b --- /dev/null +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Page.al @@ -0,0 +1,129 @@ +namespace ThirdPartyPublisher.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..b9b4820c --- /dev/null +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Table.al @@ -0,0 +1,30 @@ +namespace ThirdPartyPublisher.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 + { + 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 From db18ddbae343463caa0e55421999216aa26d20b0 Mon Sep 17 00:00:00 2001 From: Sotiris Dragonas Date: Fri, 20 Feb 2026 10:08:36 -0800 Subject: [PATCH 2/7] fix namespace --- samples/BCAgents/SalesValidationAgent/app.json | 2 +- .../app/Integration/SalesValAgentInstall.Codeunit.al | 4 ++-- .../app/Integration/SalesValCopilotCapability.EnumExt.al | 2 +- .../app/Interaction/SalesValAgentDatePicker.Page.al | 2 +- .../app/Interaction/SalesValAgentSalesOrders.PageExt.al | 4 ++-- .../app/Setup/KPI/SalesValAgentKPI.Page.al | 2 +- .../app/Setup/KPI/SalesValAgentKPI.Table.al | 2 +- .../app/Setup/KPI/SalesValAgentKPILogging.Codeunit.al | 4 ++-- .../app/Setup/Metadata/SalesValAgentFactory.Codeunit.al | 4 ++-- .../app/Setup/Metadata/SalesValAgentMetadata.Codeunit.al | 4 ++-- .../app/Setup/Metadata/SalesValMetadataProvider.EnumExt.al | 2 +- .../app/Setup/Profile/SVOrderProcessorRC.PageCustomization.al | 2 +- .../Profile/SVSOProcessorActivities.PageCustomization.al | 2 +- .../app/Setup/Profile/SVSalesOrder.PageCustomization.al | 2 +- .../app/Setup/Profile/SVSalesOrderList.PageCustomization.al | 2 +- .../Setup/Profile/SVSalesOrderStatistics.PageCustomization.al | 2 +- .../Setup/Profile/SVSalesOrderSubform.PageCustomization.al | 2 +- .../app/Setup/Profile/SalesValidationAgent.Profile.al | 2 +- .../app/Setup/SalesValAgentSetup.Codeunit.al | 4 ++-- .../SalesValidationAgent/app/Setup/SalesValAgentSetup.Page.al | 2 +- .../app/Setup/SalesValAgentSetup.Table.al | 2 +- 21 files changed, 27 insertions(+), 27 deletions(-) diff --git a/samples/BCAgents/SalesValidationAgent/app.json b/samples/BCAgents/SalesValidationAgent/app.json index abf5300a..b571f69d 100644 --- a/samples/BCAgents/SalesValidationAgent/app.json +++ b/samples/BCAgents/SalesValidationAgent/app.json @@ -1,7 +1,7 @@ { "id": "5878e09e-13b4-4058-a9f6-4a9ee1605d6a", "name": "SalesValidationAgent", - "publisher": "ThirdPartyPublisher", + "publisher": "Default Publisher", "version": "1.0.0.0", "brief": "", "description": "", diff --git a/samples/BCAgents/SalesValidationAgent/app/Integration/SalesValAgentInstall.Codeunit.al b/samples/BCAgents/SalesValidationAgent/app/Integration/SalesValAgentInstall.Codeunit.al index 7f0e2635..6d4de6c9 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Integration/SalesValAgentInstall.Codeunit.al +++ b/samples/BCAgents/SalesValidationAgent/app/Integration/SalesValAgentInstall.Codeunit.al @@ -1,9 +1,9 @@ -namespace ThirdPartyPublisher.SalesValidationAgent.Integration; +namespace SalesValidationAgent.Integration; using System.Agents; using System.AI; using System.Security.AccessControl; -using ThirdPartyPublisher.SalesValidationAgent.Setup; +using SalesValidationAgent.Setup; codeunit 50101 "Sales Val. Agent Install" { diff --git a/samples/BCAgents/SalesValidationAgent/app/Integration/SalesValCopilotCapability.EnumExt.al b/samples/BCAgents/SalesValidationAgent/app/Integration/SalesValCopilotCapability.EnumExt.al index fdaba8b7..8d6f3061 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Integration/SalesValCopilotCapability.EnumExt.al +++ b/samples/BCAgents/SalesValidationAgent/app/Integration/SalesValCopilotCapability.EnumExt.al @@ -1,4 +1,4 @@ -namespace ThirdPartyPublisher.SalesValidationAgent.Integration; +namespace SalesValidationAgent.Integration; using System.AI; diff --git a/samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentDatePicker.Page.al b/samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentDatePicker.Page.al index 9187d79d..26fdd63a 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentDatePicker.Page.al +++ b/samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentDatePicker.Page.al @@ -1,4 +1,4 @@ -namespace ThirdPartyPublisher.SalesValidationAgent.Interaction; +namespace SalesValidationAgent.Interaction; /// /// A simple dialog page that prompts the user to select a shipment date diff --git a/samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentSalesOrders.PageExt.al b/samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentSalesOrders.PageExt.al index 4abed5f8..3fb188a1 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentSalesOrders.PageExt.al +++ b/samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentSalesOrders.PageExt.al @@ -1,8 +1,8 @@ -namespace ThirdPartyPublisher.SalesValidationAgent.Interaction; +namespace SalesValidationAgent.Interaction; using Microsoft.Sales.Document; using System.Agents; -using ThirdPartyPublisher.SalesValidationAgent.Setup; +using SalesValidationAgent.Setup; /// /// Extends the Sales Order List page to enable Sales Validation Agent task assignment. diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPI.Page.al b/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPI.Page.al index 65ace51c..e87b852d 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPI.Page.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPI.Page.al @@ -1,4 +1,4 @@ -namespace ThirdPartyPublisher.SalesValidationAgent.Setup.KPI; +namespace SalesValidationAgent.Setup.KPI; page 50104 "Sales Val. Agent KPI" { diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPI.Table.al b/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPI.Table.al index d10d596b..c5054ae5 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPI.Table.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPI.Table.al @@ -1,4 +1,4 @@ -namespace ThirdPartyPublisher.SalesValidationAgent.Setup.KPI; +namespace SalesValidationAgent.Setup.KPI; table 50101 "Sales Val. Agent KPI" { diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPILogging.Codeunit.al b/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPILogging.Codeunit.al index 00bd1d5d..0ad292ad 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPILogging.Codeunit.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPILogging.Codeunit.al @@ -1,8 +1,8 @@ -namespace ThirdPartyPublisher.SalesValidationAgent.Setup.KPI; +namespace SalesValidationAgent.Setup.KPI; using Microsoft.Sales.Document; using System.Agents; -using ThirdPartyPublisher.SalesValidationAgent.Setup; +using SalesValidationAgent.Setup; /// /// Subscribes to business events to automatically log KPI metrics diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentFactory.Codeunit.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentFactory.Codeunit.al index 3bf1bb8f..07a9be81 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentFactory.Codeunit.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentFactory.Codeunit.al @@ -1,10 +1,10 @@ -namespace ThirdPartyPublisher.SalesValidationAgent.Setup.Metadata; +namespace SalesValidationAgent.Setup.Metadata; using System.Agents; using System.AI; using System.Reflection; using System.Security.AccessControl; -using ThirdPartyPublisher.SalesValidationAgent.Setup; +using SalesValidationAgent.Setup; codeunit 50100 SalesValAgentFactory implements IAgentFactory { diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentMetadata.Codeunit.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentMetadata.Codeunit.al index f5033b8a..19b1d5a7 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentMetadata.Codeunit.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentMetadata.Codeunit.al @@ -1,7 +1,7 @@ -namespace ThirdPartyPublisher.SalesValidationAgent.Setup.Metadata; +namespace SalesValidationAgent.Setup.Metadata; using System.Agents; -using ThirdPartyPublisher.SalesValidationAgent.Setup; +using SalesValidationAgent.Setup; codeunit 50102 SalesValAgentMetadata implements IAgentMetadata { diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValMetadataProvider.EnumExt.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValMetadataProvider.EnumExt.al index e345f50a..e5606718 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValMetadataProvider.EnumExt.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValMetadataProvider.EnumExt.al @@ -1,4 +1,4 @@ -namespace ThirdPartyPublisher.SalesValidationAgent.Setup.Metadata; +namespace SalesValidationAgent.Setup.Metadata; using System.Agents; diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVOrderProcessorRC.PageCustomization.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVOrderProcessorRC.PageCustomization.al index ee2149f7..bbce024b 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVOrderProcessorRC.PageCustomization.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVOrderProcessorRC.PageCustomization.al @@ -1,4 +1,4 @@ -namespace ThirdPartyPublisher.SalesValidationAgent.Setup.Profile; +namespace SalesValidationAgent.Setup.Profile; using Microsoft.Sales.RoleCenters; diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSOProcessorActivities.PageCustomization.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSOProcessorActivities.PageCustomization.al index e3a6baec..43565a6b 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSOProcessorActivities.PageCustomization.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSOProcessorActivities.PageCustomization.al @@ -1,4 +1,4 @@ -namespace ThirdPartyPublisher.SalesValidationAgent.Setup.Profile; +namespace SalesValidationAgent.Setup.Profile; using Microsoft.Sales.RoleCenters; diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrder.PageCustomization.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrder.PageCustomization.al index 6e7b5c12..8a076be9 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrder.PageCustomization.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrder.PageCustomization.al @@ -1,4 +1,4 @@ -namespace ThirdPartyPublisher.SalesValidationAgent.Setup.Profile; +namespace SalesValidationAgent.Setup.Profile; using Microsoft.Sales.Document; diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderList.PageCustomization.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderList.PageCustomization.al index f3b31834..ea6ec042 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderList.PageCustomization.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderList.PageCustomization.al @@ -1,4 +1,4 @@ -namespace ThirdPartyPublisher.SalesValidationAgent.Setup.Profile; +namespace SalesValidationAgent.Setup.Profile; using Microsoft.Sales.Document; diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderStatistics.PageCustomization.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderStatistics.PageCustomization.al index b2d99cd2..075de3d6 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderStatistics.PageCustomization.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderStatistics.PageCustomization.al @@ -1,4 +1,4 @@ -namespace ThirdPartyPublisher.SalesValidationAgent.Setup.Profile; +namespace SalesValidationAgent.Setup.Profile; using Microsoft.Sales.Document; diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderSubform.PageCustomization.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderSubform.PageCustomization.al index cf13dbdf..dffadf75 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderSubform.PageCustomization.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderSubform.PageCustomization.al @@ -1,4 +1,4 @@ -namespace ThirdPartyPublisher.SalesValidationAgent.Setup.Profile; +namespace SalesValidationAgent.Setup.Profile; using Microsoft.Sales.Document; diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SalesValidationAgent.Profile.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SalesValidationAgent.Profile.al index 648f07bb..d2ea31c5 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SalesValidationAgent.Profile.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SalesValidationAgent.Profile.al @@ -1,4 +1,4 @@ -namespace ThirdPartyPublisher.SalesValidationAgent.Setup.Profile; +namespace SalesValidationAgent.Setup.Profile; using Microsoft.Sales.RoleCenters; diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Codeunit.al b/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Codeunit.al index 969e4187..61093419 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Codeunit.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Codeunit.al @@ -1,9 +1,9 @@ -namespace ThirdPartyPublisher.SalesValidationAgent.Setup; +namespace SalesValidationAgent.Setup; using System.Agents; using System.Reflection; using System.Security.AccessControl; -using ThirdPartyPublisher.SalesValidationAgent.Setup.KPI; +using SalesValidationAgent.Setup.KPI; codeunit 50103 "Sales Val. Agent Setup" { diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Page.al b/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Page.al index 29b35b1b..26bb2133 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Page.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Page.al @@ -1,4 +1,4 @@ -namespace ThirdPartyPublisher.SalesValidationAgent.Setup; +namespace SalesValidationAgent.Setup; using System.Agents; using System.AI; diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Table.al b/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Table.al index b9b4820c..5d724ed2 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Table.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Table.al @@ -1,4 +1,4 @@ -namespace ThirdPartyPublisher.SalesValidationAgent.Setup; +namespace SalesValidationAgent.Setup; table 50100 "Sales Val. Agent Setup" { From c611d253b12bdccd830dc36aef28b6bd36edca21 Mon Sep 17 00:00:00 2001 From: Sotiris Dragonas Date: Mon, 23 Feb 2026 10:34:49 +0100 Subject: [PATCH 3/7] Address PR review comments - Add MIT copyright headers to all .al files - Fix using statement ordering (alphabetical) across all files - Use fluent AgentTaskBuilder chaining syntax - Fix IF -> if casing - Remove SingleInstance from KPI logging codeunit (no global state) - Use IsEmpty() instead of not FindFirst() in factory - Clarify ShowCanCreateAgent is UI-only, not a hard single-instance guard - Enable agent profile (Enabled = true) - Remove redundant 'internal' keyword from Setup codeunit methods - Add comments explaining KPI page/table and Setup table interface contract - Rename app to 'Sales Validation Agent Sample' in app.json - Fix README: single-instance claim, Preview auto-enabled, avatar/role center mention Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- samples/BCAgents/SalesValidationAgent/README.md | 11 +++++------ samples/BCAgents/SalesValidationAgent/app.json | 2 +- .../SalesValAgentInstall.Codeunit.al | 7 ++++++- .../SalesValCopilotCapability.EnumExt.al | 5 +++++ .../Interaction/SalesValAgentDatePicker.Page.al | 5 +++++ .../SalesValAgentSalesOrders.PageExt.al | 17 +++++++++++------ .../app/Setup/KPI/SalesValAgentKPI.Page.al | 11 +++++++++++ .../app/Setup/KPI/SalesValAgentKPI.Table.al | 8 ++++++++ .../KPI/SalesValAgentKPILogging.Codeunit.al | 8 ++++++-- .../Metadata/SalesValAgentFactory.Codeunit.al | 11 ++++++++--- .../Metadata/SalesValAgentMetadata.Codeunit.al | 7 ++++++- .../SalesValMetadataProvider.EnumExt.al | 5 +++++ .../SVOrderProcessorRC.PageCustomization.al | 5 +++++ ...SVSOProcessorActivities.PageCustomization.al | 5 +++++ .../Profile/SVSalesOrder.PageCustomization.al | 5 +++++ .../SVSalesOrderList.PageCustomization.al | 5 +++++ .../SVSalesOrderStatistics.PageCustomization.al | 5 +++++ .../SVSalesOrderSubform.PageCustomization.al | 5 +++++ .../Profile/SalesValidationAgent.Profile.al | 7 ++++++- .../app/Setup/SalesValAgentSetup.Codeunit.al | 11 ++++++++--- .../app/Setup/SalesValAgentSetup.Page.al | 5 +++++ .../app/Setup/SalesValAgentSetup.Table.al | 8 ++++++++ 22 files changed, 134 insertions(+), 24 deletions(-) diff --git a/samples/BCAgents/SalesValidationAgent/README.md b/samples/BCAgents/SalesValidationAgent/README.md index d3295327..d7e6d078 100644 --- a/samples/BCAgents/SalesValidationAgent/README.md +++ b/samples/BCAgents/SalesValidationAgent/README.md @@ -41,7 +41,7 @@ Tracks agent performance metrics. A KPI table records counters per agent user (c #### 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, and enforces a **single-instance** constraint (only one agent can be created). 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. +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 (single-instance by convention); if the app needs to strictly enforce one agent, the Setup table/page must also prevent duplicates. 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 @@ -51,11 +51,10 @@ Defines a dedicated **Sales Validation Agent (Copilot)** profile based on the *O ## How It Works -1. **Enable the capability** – An admin enables *Sales Validation Agent* in the Copilot & AI Capabilities page. -2. **Create the agent** – Open the *Sales Val. Agent Setup* configuration dialog to provision the agent user with the correct profile and permissions. -3. **Assign a task** – From the **Sales Order List**, choose *Validate with Agent*, pick a shipment date, and a task is created for the agent. -4. **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. -5. **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. +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. --- diff --git a/samples/BCAgents/SalesValidationAgent/app.json b/samples/BCAgents/SalesValidationAgent/app.json index b571f69d..4e0dbc72 100644 --- a/samples/BCAgents/SalesValidationAgent/app.json +++ b/samples/BCAgents/SalesValidationAgent/app.json @@ -1,6 +1,6 @@ { "id": "5878e09e-13b4-4058-a9f6-4a9ee1605d6a", - "name": "SalesValidationAgent", + "name": "Sales Validation Agent Sample", "publisher": "Default Publisher", "version": "1.0.0.0", "brief": "", diff --git a/samples/BCAgents/SalesValidationAgent/app/Integration/SalesValAgentInstall.Codeunit.al b/samples/BCAgents/SalesValidationAgent/app/Integration/SalesValAgentInstall.Codeunit.al index 6d4de6c9..8e8a6ff5 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Integration/SalesValAgentInstall.Codeunit.al +++ b/samples/BCAgents/SalesValidationAgent/app/Integration/SalesValAgentInstall.Codeunit.al @@ -1,9 +1,14 @@ +// ------------------------------------------------------------------------------------------------ +// 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; -using SalesValidationAgent.Setup; codeunit 50101 "Sales Val. Agent Install" { diff --git a/samples/BCAgents/SalesValidationAgent/app/Integration/SalesValCopilotCapability.EnumExt.al b/samples/BCAgents/SalesValidationAgent/app/Integration/SalesValCopilotCapability.EnumExt.al index 8d6f3061..e8d759ba 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Integration/SalesValCopilotCapability.EnumExt.al +++ b/samples/BCAgents/SalesValidationAgent/app/Integration/SalesValCopilotCapability.EnumExt.al @@ -1,3 +1,8 @@ +// ------------------------------------------------------------------------------------------------ +// 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; diff --git a/samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentDatePicker.Page.al b/samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentDatePicker.Page.al index 26fdd63a..44058361 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentDatePicker.Page.al +++ b/samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentDatePicker.Page.al @@ -1,3 +1,8 @@ +// ------------------------------------------------------------------------------------------------ +// 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; /// diff --git a/samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentSalesOrders.PageExt.al b/samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentSalesOrders.PageExt.al index 3fb188a1..e4509d6b 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentSalesOrders.PageExt.al +++ b/samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentSalesOrders.PageExt.al @@ -1,8 +1,13 @@ +// ------------------------------------------------------------------------------------------------ +// 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 System.Agents; using SalesValidationAgent.Setup; +using System.Agents; /// /// Extends the Sales Order List page to enable Sales Validation Agent task assignment. @@ -35,7 +40,7 @@ pageextension 50101 "Sales Val. Agent Sales Orders" extends "Sales Order List" ShipmentDate: Date; ExternalId: Text; begin - IF not SalesValAgentSetup.TryGetAgent(AgentUserSecurityId) then + if not SalesValAgentSetup.TryGetAgent(AgentUserSecurityId) then Error(SVAgentDoesNotExistErr); if not Agent.IsActive(AgentUserSecurityId) then @@ -53,11 +58,11 @@ pageextension 50101 "Sales Val. Agent Sales Orders" extends "Sales Order List" TaskTitle := CopyStr(StrSubstNo(TaskTitleLbl, ShipmentDate), 1, MaxStrLen(TaskTitle)); From := CopyStr(UserId(), 1, MaxStrLen(From)); - AgentTaskBuilder.Initialize(AgentUserSecurityId, TaskTitle); ExternalId := Format(CreateGuid()); - AgentTaskBuilder.SetExternalId(ExternalId); - AgentTaskBuilder.AddTaskMessage(From, Message); - AgentTaskBuilder.Create(); + AgentTaskBuilder.Initialize(AgentUserSecurityId, TaskTitle) + .SetExternalId(ExternalId) + .AddTaskMessage(From, Message) + .Create(); AgentTask.ReadIsolation(IsolationLevel::ReadCommitted); AgentTask.SetRange("External ID", ExternalId); diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPI.Page.al b/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPI.Page.al index e87b852d..431649f9 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPI.Page.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPI.Page.al @@ -1,3 +1,8 @@ +// ------------------------------------------------------------------------------------------------ +// 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" @@ -32,6 +37,12 @@ page 50104 "Sales Val. Agent KPI" 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; diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPI.Table.al b/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPI.Table.al index c5054ae5..af3270a6 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPI.Table.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPI.Table.al @@ -1,3 +1,8 @@ +// ------------------------------------------------------------------------------------------------ +// 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" @@ -12,6 +17,9 @@ table 50101 "Sales Val. Agent KPI" 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'; diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPILogging.Codeunit.al b/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPILogging.Codeunit.al index 0ad292ad..7117806b 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPILogging.Codeunit.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPILogging.Codeunit.al @@ -1,8 +1,13 @@ +// ------------------------------------------------------------------------------------------------ +// 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 System.Agents; using SalesValidationAgent.Setup; +using System.Agents; /// /// Subscribes to business events to automatically log KPI metrics @@ -13,7 +18,6 @@ using SalesValidationAgent.Setup; codeunit 50106 "Sales Val. Agent KPI Logging" { Access = Internal; - SingleInstance = true; [EventSubscriber(ObjectType::Codeunit, Codeunit::"Release Sales Document", OnAfterReleaseSalesDoc, '', false, false)] local procedure OnAfterReleaseSalesDoc(var SalesHeader: Record "Sales Header") diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentFactory.Codeunit.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentFactory.Codeunit.al index 07a9be81..c5894b5f 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentFactory.Codeunit.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentFactory.Codeunit.al @@ -1,10 +1,15 @@ +// ------------------------------------------------------------------------------------------------ +// 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; -using SalesValidationAgent.Setup; codeunit 50100 SalesValAgentFactory implements IAgentFactory { @@ -24,8 +29,8 @@ codeunit 50100 SalesValAgentFactory implements IAgentFactory var SalesValAgentSetupRec: Record "Sales Val. Agent Setup"; begin - // Single instance agent - exit(not SalesValAgentSetupRec.FindFirst()); + // ShowCanCreateAgent controls UI visibility only; to truly enforce single-instance, the Setup table/page also needs to prevent duplicate records. + exit(SalesValAgentSetupRec.IsEmpty()); end; procedure GetCopilotCapability(): Enum "Copilot Capability" diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentMetadata.Codeunit.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentMetadata.Codeunit.al index 19b1d5a7..357e0623 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentMetadata.Codeunit.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentMetadata.Codeunit.al @@ -1,7 +1,12 @@ +// ------------------------------------------------------------------------------------------------ +// 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; using SalesValidationAgent.Setup; +using System.Agents; codeunit 50102 SalesValAgentMetadata implements IAgentMetadata { diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValMetadataProvider.EnumExt.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValMetadataProvider.EnumExt.al index e5606718..14a14701 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValMetadataProvider.EnumExt.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValMetadataProvider.EnumExt.al @@ -1,3 +1,8 @@ +// ------------------------------------------------------------------------------------------------ +// 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; diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVOrderProcessorRC.PageCustomization.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVOrderProcessorRC.PageCustomization.al index bbce024b..2df3d320 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVOrderProcessorRC.PageCustomization.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVOrderProcessorRC.PageCustomization.al @@ -1,3 +1,8 @@ +// ------------------------------------------------------------------------------------------------ +// 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; diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSOProcessorActivities.PageCustomization.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSOProcessorActivities.PageCustomization.al index 43565a6b..4aba0666 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSOProcessorActivities.PageCustomization.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSOProcessorActivities.PageCustomization.al @@ -1,3 +1,8 @@ +// ------------------------------------------------------------------------------------------------ +// 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; diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrder.PageCustomization.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrder.PageCustomization.al index 8a076be9..08370d97 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrder.PageCustomization.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrder.PageCustomization.al @@ -1,3 +1,8 @@ +// ------------------------------------------------------------------------------------------------ +// 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; diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderList.PageCustomization.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderList.PageCustomization.al index ea6ec042..46e67a61 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderList.PageCustomization.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderList.PageCustomization.al @@ -1,3 +1,8 @@ +// ------------------------------------------------------------------------------------------------ +// 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; diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderStatistics.PageCustomization.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderStatistics.PageCustomization.al index 075de3d6..8ea47736 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderStatistics.PageCustomization.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderStatistics.PageCustomization.al @@ -1,3 +1,8 @@ +// ------------------------------------------------------------------------------------------------ +// 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; diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderSubform.PageCustomization.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderSubform.PageCustomization.al index dffadf75..8bc81393 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderSubform.PageCustomization.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderSubform.PageCustomization.al @@ -1,3 +1,8 @@ +// ------------------------------------------------------------------------------------------------ +// 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; diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SalesValidationAgent.Profile.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SalesValidationAgent.Profile.al index d2ea31c5..2d786671 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SalesValidationAgent.Profile.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SalesValidationAgent.Profile.al @@ -1,3 +1,8 @@ +// ------------------------------------------------------------------------------------------------ +// 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; @@ -5,7 +10,7 @@ using Microsoft.Sales.RoleCenters; profile "Sales Validation Agent" { Caption = 'Sales Validation Agent (Copilot)'; - Enabled = false; + Enabled = true; ProfileDescription = 'Functionality for the Sales Validation Agent to efficiently validate and process sales orders.'; Promoted = false; RoleCenter = "Order Processor Role Center"; diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Codeunit.al b/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Codeunit.al index 61093419..8801e225 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Codeunit.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Codeunit.al @@ -1,9 +1,14 @@ +// ------------------------------------------------------------------------------------------------ +// 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; -using SalesValidationAgent.Setup.KPI; codeunit 50103 "Sales Val. Agent Setup" { @@ -55,7 +60,7 @@ codeunit 50103 "Sales Val. Agent Setup" exit(Instructions); end; - internal procedure GetDefaultProfile(var TempAllProfile: Record "All Profile" temporary) + procedure GetDefaultProfile(var TempAllProfile: Record "All Profile" temporary) var CurrentModuleInfo: ModuleInfo; begin @@ -63,7 +68,7 @@ codeunit 50103 "Sales Val. Agent Setup" Agent.PopulateDefaultProfile(DefaultProfileTok, CurrentModuleInfo.Id, TempAllProfile); end; - internal procedure GetDefaultAccessControls(var TempAccessControlBuffer: Record "Access Control Buffer" temporary) + procedure GetDefaultAccessControls(var TempAccessControlBuffer: Record "Access Control Buffer" temporary) begin Clear(TempAccessControlBuffer); TempAccessControlBuffer."Company Name" := CopyStr(CompanyName(), 1, MaxStrLen(TempAccessControlBuffer."Company Name")); diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Page.al b/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Page.al index 26bb2133..a34eead7 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Page.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Page.al @@ -1,3 +1,8 @@ +// ------------------------------------------------------------------------------------------------ +// 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; diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Table.al b/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Table.al index 5d724ed2..ba3c0473 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Table.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/SalesValAgentSetup.Table.al @@ -1,3 +1,8 @@ +// ------------------------------------------------------------------------------------------------ +// 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" @@ -12,6 +17,8 @@ table 50100 "Sales Val. Agent Setup" 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'; @@ -20,6 +27,7 @@ table 50100 "Sales Val. Agent Setup" Editable = false; } } + keys { key(Key1; "User Security ID") From 78d5f00e185b5ec8bb91579727c7e5c2c2dbe8d2 Mon Sep 17 00:00:00 2001 From: Sotiris Dragonas Date: Mon, 23 Feb 2026 10:36:22 +0100 Subject: [PATCH 4/7] remove external ID --- .../Interaction/SalesValAgentSalesOrders.PageExt.al | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentSalesOrders.PageExt.al b/samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentSalesOrders.PageExt.al index e4509d6b..6b043ef2 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentSalesOrders.PageExt.al +++ b/samples/BCAgents/SalesValidationAgent/app/Interaction/SalesValAgentSalesOrders.PageExt.al @@ -38,7 +38,6 @@ pageextension 50101 "Sales Val. Agent Sales Orders" extends "Sales Order List" From: Text[250]; Message: Text; ShipmentDate: Date; - ExternalId: Text; begin if not SalesValAgentSetup.TryGetAgent(AgentUserSecurityId) then Error(SVAgentDoesNotExistErr); @@ -58,17 +57,10 @@ pageextension 50101 "Sales Val. Agent Sales Orders" extends "Sales Order List" TaskTitle := CopyStr(StrSubstNo(TaskTitleLbl, ShipmentDate), 1, MaxStrLen(TaskTitle)); From := CopyStr(UserId(), 1, MaxStrLen(From)); - ExternalId := Format(CreateGuid()); - AgentTaskBuilder.Initialize(AgentUserSecurityId, TaskTitle) - .SetExternalId(ExternalId) + AgentTask := AgentTaskBuilder.Initialize(AgentUserSecurityId, TaskTitle) .AddTaskMessage(From, Message) .Create(); - AgentTask.ReadIsolation(IsolationLevel::ReadCommitted); - AgentTask.SetRange("External ID", ExternalId); - if not AgentTask.FindFirst() then - Error(TaskCreateFailedErr); - Message(TaskAssignedMsg, AgentTask.ID, ShipmentDate); end; } @@ -79,7 +71,6 @@ pageextension 50101 "Sales Val. Agent Sales Orders" extends "Sales Order List" 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.'; - TaskCreateFailedErr: Label 'The agent task could not be created.'; 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'; From 0b787da202e89ab2801e5c61109c5f71c7f82c13 Mon Sep 17 00:00:00 2001 From: Sotiris Dragonas Date: Mon, 23 Feb 2026 10:39:23 +0100 Subject: [PATCH 5/7] openup names --- .../app/Setup/Profile/SVOrderProcessorRC.PageCustomization.al | 2 +- .../Setup/Profile/SVSOProcessorActivities.PageCustomization.al | 2 +- .../app/Setup/Profile/SVSalesOrder.PageCustomization.al | 2 +- .../app/Setup/Profile/SVSalesOrderList.PageCustomization.al | 2 +- .../Setup/Profile/SVSalesOrderStatistics.PageCustomization.al | 2 +- .../app/Setup/Profile/SVSalesOrderSubform.PageCustomization.al | 2 +- .../app/Setup/Profile/SalesValidationAgent.Profile.al | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVOrderProcessorRC.PageCustomization.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVOrderProcessorRC.PageCustomization.al index 2df3d320..90f9c044 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVOrderProcessorRC.PageCustomization.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVOrderProcessorRC.PageCustomization.al @@ -8,7 +8,7 @@ namespace SalesValidationAgent.Setup.Profile; using Microsoft.Sales.RoleCenters; -pagecustomization SVOrderProcessorRC customizes "Order Processor Role Center" +pagecustomization "SV Order Processor RC" customizes "Order Processor Role Center" { ClearLayout = true; ClearActions = true; diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSOProcessorActivities.PageCustomization.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSOProcessorActivities.PageCustomization.al index 4aba0666..a16b0e20 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSOProcessorActivities.PageCustomization.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSOProcessorActivities.PageCustomization.al @@ -7,7 +7,7 @@ namespace SalesValidationAgent.Setup.Profile; using Microsoft.Sales.RoleCenters; -pagecustomization SVSOProcessorActivities customizes "SO Processor Activities" +pagecustomization "SV SO Processor Activities" customizes "SO Processor Activities" { ClearLayout = true; ClearActions = true; diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrder.PageCustomization.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrder.PageCustomization.al index 08370d97..4d4a0bca 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrder.PageCustomization.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrder.PageCustomization.al @@ -7,7 +7,7 @@ namespace SalesValidationAgent.Setup.Profile; using Microsoft.Sales.Document; -pagecustomization SVSalesOrder customizes "Sales Order" +pagecustomization "SV Sales Order" customizes "Sales Order" { ClearLayout = true; ClearActions = true; diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderList.PageCustomization.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderList.PageCustomization.al index 46e67a61..8d3ed048 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderList.PageCustomization.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderList.PageCustomization.al @@ -7,7 +7,7 @@ namespace SalesValidationAgent.Setup.Profile; using Microsoft.Sales.Document; -pagecustomization SVSalesOrderList customizes "Sales Order List" +pagecustomization "SV Sales Order List" customizes "Sales Order List" { ClearLayout = true; ClearActions = true; diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderStatistics.PageCustomization.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderStatistics.PageCustomization.al index 8ea47736..c4b0d3f7 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderStatistics.PageCustomization.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderStatistics.PageCustomization.al @@ -7,7 +7,7 @@ namespace SalesValidationAgent.Setup.Profile; using Microsoft.Sales.Document; -pagecustomization SVSalesOrderStatistics customizes "Sales Order Statistics" +pagecustomization "SV Sales Order Statistics" customizes "Sales Order Statistics" { ClearLayout = true; ClearActions = true; diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderSubform.PageCustomization.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderSubform.PageCustomization.al index 8bc81393..ca783c26 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderSubform.PageCustomization.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SVSalesOrderSubform.PageCustomization.al @@ -7,7 +7,7 @@ namespace SalesValidationAgent.Setup.Profile; using Microsoft.Sales.Document; -pagecustomization SVSalesOrderSubform customizes "Sales Order Subform" +pagecustomization "SV Sales Order Subform" customizes "Sales Order Subform" { ClearLayout = true; ClearActions = true; diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SalesValidationAgent.Profile.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SalesValidationAgent.Profile.al index 2d786671..8dd14bdf 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SalesValidationAgent.Profile.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Profile/SalesValidationAgent.Profile.al @@ -14,5 +14,5 @@ profile "Sales Validation Agent" ProfileDescription = 'Functionality for the Sales Validation Agent to efficiently validate and process sales orders.'; Promoted = false; RoleCenter = "Order Processor Role Center"; - Customizations = SVOrderProcessorRC, SVSalesOrder, SVSalesOrderStatistics, SVSOProcessorActivities, SVSalesOrderList, SVSalesOrderSubform; + Customizations = "SV Order Processor RC", "SV Sales Order", "SV Sales Order Statistics", "SV SO Processor Activities", "SV Sales Order List", "SV Sales Order Subform"; } From c4533a014654667ed7b530757c81d123e9ac0db6 Mon Sep 17 00:00:00 2001 From: Sotiris Dragonas Date: Mon, 23 Feb 2026 10:42:29 +0100 Subject: [PATCH 6/7] fix --- samples/BCAgents/SalesValidationAgent/README.md | 2 +- .../app/Setup/Metadata/SalesValAgentFactory.Codeunit.al | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/samples/BCAgents/SalesValidationAgent/README.md b/samples/BCAgents/SalesValidationAgent/README.md index d7e6d078..f72fc296 100644 --- a/samples/BCAgents/SalesValidationAgent/README.md +++ b/samples/BCAgents/SalesValidationAgent/README.md @@ -41,7 +41,7 @@ Tracks agent performance metrics. A KPI table records counters per agent user (c #### 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 (single-instance by convention); if the app needs to strictly enforce one agent, the Setup table/page must also prevent duplicates. 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. +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 diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentFactory.Codeunit.al b/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentFactory.Codeunit.al index c5894b5f..ec59c964 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentFactory.Codeunit.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/Metadata/SalesValAgentFactory.Codeunit.al @@ -29,7 +29,6 @@ codeunit 50100 SalesValAgentFactory implements IAgentFactory var SalesValAgentSetupRec: Record "Sales Val. Agent Setup"; begin - // ShowCanCreateAgent controls UI visibility only; to truly enforce single-instance, the Setup table/page also needs to prevent duplicate records. exit(SalesValAgentSetupRec.IsEmpty()); end; From c998e2c7477b1ceda58fc7b26f947112c2217c35 Mon Sep 17 00:00:00 2001 From: Sotiris Dragonas Date: Mon, 23 Feb 2026 10:44:28 +0100 Subject: [PATCH 7/7] fix event subscriber --- .../app/Setup/KPI/SalesValAgentKPILogging.Codeunit.al | 2 ++ 1 file changed, 2 insertions(+) diff --git a/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPILogging.Codeunit.al b/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPILogging.Codeunit.al index 7117806b..8b117c11 100644 --- a/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPILogging.Codeunit.al +++ b/samples/BCAgents/SalesValidationAgent/app/Setup/KPI/SalesValAgentKPILogging.Codeunit.al @@ -18,6 +18,8 @@ using System.Agents; 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")